<commit_before>let _ = require('lodash')
let path = require('path')
let glob = require('glob')
let async = require('async')

module.exports = function flattenFileList (hash, folderPath, zipPath) {
  return new Promise((resolve, reject) => {
    if (!folderPath) {
      throw new Error('Package directory not specified!')
    }

    // Convert string values to arrays with a single element.
    let normalizedHash = _.mapValues(hash, _.castArray)

    // Each value of the hash is an array of globs. Let's deglob asynchronously.
    async.mapValuesSeries(normalizedHash, (globList, key, doneList) => {
      async.mapSeries(globList, (oneGlob, doneOne) => {
        if (oneGlob === '@') return setImmediate(() => doneOne(null, zipPath))
        glob(oneGlob, {cwd: folderPath}, doneOne)
      }, doneList)
    }, (err, deglobbedHash) => {
      if (err) throw err

      let flattenedHash = _(deglobbedHash)
        // Now each value is an array of arrays of file paths. Let's flatten.
        .mapValues(_.flatten)
        // Let's also remove nulls (globs with nothing found).
        .mapValues(_.compact)
        // Remove keys with empty lists.
        .omitBy(_.isEmpty)
        // Normalize file paths.
        .mapValues(fileList => _.map(fileList, path.normalize))
        // Merge lists with same normalized destination.
        .transform((result, fromList, toKey) => {
          let normalizedTo = path.normalize(toKey)
          result[normalizedTo] = (result[normalizedTo] || []).concat(fromList)
        })
        // Remove duplicate file paths.
        .mapValues(_.uniq)
        // End the chain, get result.
        .value()

      // Some file paths can be malicious or just dumb (copy all from `/`) - let's check.
      try {
        let thingsToCheckForMaliciousPaths = [
          {array: _.keys(flattenedHash), preposition: 'to'},
          {array: _.flatten(_.values(flattenedHash)), preposition: 'from'}
        ]
        thingsToCheckForMaliciousPaths.forEach(thing => {
          if (thing.array.some(item => item.startsWith(`..${path.sep}`) || item === '..')) {
            throw new Error(`Trying to copy ${thing.preposition} outside of Minecraft!`)
          }
          if (thing.array.some(path.isAbsolute)) {
            throw new Error(`Trying to copy ${thing.preposition} an absolute path!`)
          }
        })
      } catch (error) {
        reject(error)
      }

      resolve(flattenedHash)
    })
  })
}
<commit_msg>fix(install/flattenFileHash): allow absolute path to the zip (@)<commit_after>let _ = require('lodash')
let path = require('path')
let glob = require('glob')
let async = require('async')

module.exports = function flattenFileList (hash, folderPath, zipPath) {
  return new Promise((resolve, reject) => {
    if (!folderPath) {
      throw new Error('Package directory not specified!')
    }

    // Convert string values to arrays with a single element.
    let normalizedHash = _.mapValues(hash, _.castArray)

    // Each value of the hash is an array of globs. Let's deglob asynchronously.
    async.mapValuesSeries(normalizedHash, (globList, key, doneList) => {
      async.mapSeries(globList, (oneGlob, doneOne) => {
        if (oneGlob === '@') return setImmediate(() => doneOne(null, zipPath))
        glob(oneGlob, {cwd: folderPath}, doneOne)
      }, doneList)
    }, (err, deglobbedHash) => {
      if (err) throw err

      let flattenedHash = _(deglobbedHash)
        // Now each value is an array of arrays of file paths. Let's flatten.
        .mapValues(_.flatten)
        // Let's also remove nulls (globs with nothing found).
        .mapValues(_.compact)
        // Remove keys with empty lists.
        .omitBy(_.isEmpty)
        // Normalize file paths.
        .mapValues(fileList => _.map(fileList, path.normalize))
        // Merge lists with same normalized destination.
        .transform((result, fromList, toKey) => {
          let normalizedTo = path.normalize(toKey)
          result[normalizedTo] = (result[normalizedTo] || []).concat(fromList)
        })
        // Remove duplicate file paths.
        .mapValues(_.uniq)
        // End the chain, get result.
        .value()

      // Some file paths can be malicious or just dumb (copy all from `/`) - let's check.
      try {
        let thingsToCheckForMaliciousPaths = [
          {array: _.keys(flattenedHash), preposition: 'to'},
          {array: _.flatten(_.values(flattenedHash)), preposition: 'from'}
        ]
        thingsToCheckForMaliciousPaths.forEach(thing => {
          if (thing.array.some(item => item.startsWith(`..${path.sep}`) || item === '..')) {
            throw new Error(`Trying to copy ${thing.preposition} outside of Minecraft!`)
          }
          if (thing.array.some(p => (path.isAbsolute(p) && p !== zipPath))) {
            throw new Error(`Trying to copy ${thing.preposition} an absolute path!`)
          }
        })
      } catch (error) {
        reject(error)
      }

      resolve(flattenedHash)
    })
  })
}

{'fraction_non_alphanumeric': 0.132, 'fraction_numerical': 0.0, 'mean_word_length': 2.4656964656964657, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1758974', 'n_tokens_mistral': 1472, 'n_tokens_neox': 1440, 'n_words': 404}