HEX
Server: Apache
System: Linux webd004.cluster130.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64
User: frenchy (106757)
PHP: 7.4.33
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/f/r/e/frenchy/www/french-american.org/current/node_modules/@snyk/gemfile/gemfile.js
'use strict';

const fs = require('fs');
const path = require('path');
const assert = require('assert');

const GEMFILE_DEFAULT_LOCATION = path.resolve(process.cwd(), 'Gemfile.lock');
const WHITESPACE = /^(\s*)/;
const GEMFILE_KEY_VALUE = /^\s*([^:(]*)\s*\:*\s*(.*)/;
const ORIGINS = ['GEM', 'GIT', 'PATH'];
const RUBY = /^ruby\s(.*)/;

module.exports = {
  interpret,
  parse,
  parseSync
};

function interpret(string, extractMeta) {
  assert(
    typeof string === 'string',
    'gemfile.interpret expects a UTF-8 Gemfile.lock string source.'
  );

  const gemfileMeta = {};

  let line;
  let index = 0;
  let previousWhitespace = -1;
  let keyCount = {};
  let gemfile = {};
  let lines = string.split('\n');
  let stack = [];

  while((line = lines[index++]) !== undefined) {

    // Handle depth stack changes

    let whitespace = WHITESPACE.exec(line)[1].length;

    if (whitespace <= previousWhitespace) {
      let stackIndex = stack.length - 1;

      while(stack[stackIndex] && (whitespace <= stack[stackIndex].depth)) {
        stack.pop();
        stackIndex--;
      }
    }

    // Make note of line's whitespace depth

    previousWhitespace = whitespace;

    // Handle new key/value leaf

    let parts = GEMFILE_KEY_VALUE.exec(line);
    let key = parts[1].trim();
    let value = parts[2] || '';

    if (key) {

      // Handle path traversal

      let level = gemfile;

      for (let stackIndex = 0; stackIndex < stack.length; stackIndex++) {
        if (level[stack[stackIndex].key]) {
          level = level[stack[stackIndex].key];
        }
      }

      // Handle data type inference

      let data = {};

      if (value.indexOf('/') > -1)  {
        data.path = value;
      } else if (value.indexOf('(') > -1) {
        if (value[value.length - 1] === '!') {
          value = value.substring(0, value.length - 1);
          data.outsourced = true;
        }

        if (value[1] !== ')') {
          data.version = value.substring(1, value.length - 1);
        }
      } else if (/\b[0-9a-f]{7,40}\b/.test(value)) {
        data.sha = value;
      }

      // Set key at current level

      // Do not throw away additional top-level key entries
      // e.g. multiple GIT/GEM blocks
      if (level[key]) {
        if (keyCount[key] === undefined) {
          keyCount[key] = 0;
        } else {
          keyCount[key]++;
        }
        level[key + keyCount[key]] = level[key];
      }
      level[key] = data;

      // Push key on stack

      stack.push({key, depth: whitespace});
    }
  }

  let keys = Object.keys(gemfile);

  let hasGemKey = keys.indexOf('GEM') > -1;
  let hasDependenciesKey = keys.indexOf('DEPENDENCIES') > -1;
  let hasPlatformsKey = keys.indexOf('PLATFORMS') > -1;

  if (!hasGemKey || !hasDependenciesKey || !hasPlatformsKey) {
    console.warn([
      'Are you sure this a Gemfile.lock?',
      'If it is, please file an issue on Github: https://github.com/treycordova/gemfile/issues.',
      'Regardless, gemfile parsed whatever you gave it.'
    ].join('\n'));
  }


  if (gemfile['BUNDLED WITH']) {
    gemfile['BUNDLED WITH'] = Object.keys(gemfile['BUNDLED WITH'])[0];
  }

  if (gemfile['RUBY VERSION']) {
    const rawVersion= Object.keys(gemfile['RUBY VERSION'])[0];
    const version = RUBY.exec(rawVersion)[1];
    gemfile['RUBY VERSION'] = version;
  }

  if (extractMeta) {
    gemfileMeta.bundledWith = gemfile['BUNDLED WITH'];
    gemfileMeta.rubyVersion = gemfile['RUBY VERSION'];
    gemfileMeta.platforms = gemfile['PLATFORMS'];
    gemfileMeta.dependencies = gemfile['DEPENDENCIES'];
    gemfileMeta.specs = Object.keys(gemfile)
                              .filter(key =>
                                ORIGINS.some(origin => key.startsWith(origin)))
                              .reduce((specs, key) => {
                                const type = key.match(/[A-Z]+/)[0];
                                const meta = Object.assign({ type }, gemfile[key]);
                                delete meta.specs;
                                Object.assign(specs, Object.keys(gemfile[key].specs).reduce((specs, gem) => {
                                  specs[gem] = Object.assign({}, gemfile[key].specs[gem], meta);
                                  return specs;
                                }, {}));
                                return specs;
                              }, {});
    return gemfileMeta;
  }

  return gemfile;
}

function parse(path, extractMeta) {
  path = typeof path === 'string' ?
    path :
    GEMFILE_DEFAULT_LOCATION;

  return new Promise(function(resolve, reject) {
    let file = fs.readFile(path, {encoding: 'utf8'}, function(error, gemfile) {
      if (error) {
        return reject(`Couldn't find a Gemfile at the specified location: ${path}.`);
      } else {
        return resolve(interpret(gemfile, extractMeta));
      }
    });
  });
}

function parseSync(path, extractMeta) {
  path = typeof path === 'string' ?
    path :
    GEMFILE_DEFAULT_LOCATION;

  return interpret(fs.readFileSync(path, 'utf8'), extractMeta);
}