<commit_before>'use strict';

const _                   = require('underscore'),
      os                  = require('os'),
      fs                  = require('fs'),
      http                = require('http'),
      https               = require('https'),
      path                = require('path'),
      yargs               = require('yargs'),
      IdPMetadata        = require('./idp-metadata'),
      App                 = require('../app/app');

/**
 * Globals
 */

const NAMEID_FORMAT_PREFERENCE = [
  'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
  'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
  'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
  'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
  'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos',
  'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName'
]

const BINDINGS = {
  REDIRECT: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
  POST: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
}

const KEY_CERT_HELP_TEXT = "Please generate a key-pair for the SP using the following openssl command:\n" +
      "\topenssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Service Provider' -keyout ./config/sp-key.pem -out ./config/sp-cert.pem -days 7300";

const PEM_TYPES = {
  certificate: /-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----/,
  privateKey: /-----BEGIN RSA PRIVATE KEY-----\n[^-]*\n-----END RSA PRIVATE KEY-----/,
  publicKey: /-----BEGIN PUBLIC KEY-----\n[^-]*\n-----END PUBLIC KEY-----/,
};

function matchesCertType(value, type) {
  // console.info(`Testing ${PEM_TYPES[type].toString()} against "${value}"`);
  // console.info(`result: ${PEM_TYPES[type] && PEM_TYPES[type].test(value)}`);
  return PEM_TYPES[type] && PEM_TYPES[type].test(value);
}

function bufferFromString(value) {
  if (Buffer.hasOwnProperty('from')) {
    // node 6+
    return Buffer.from(value);
  } else {
    return new Buffer(value);
  }
}

function resolveFilePath(filePath) {
  var possiblePath;
  console.log('resolving path %s', filePath);
  if (fs.existsSync(filePath)) {
    return filePath;
  }
  if (filePath.slice(0, 2) === '~/') {
    possiblePath = path.resolve(process.env.HOME, filePath.slice(2));
    if (fs.existsSync(possiblePath)) {
      return possiblePath;
    } else {
      // for ~/ paths, don't try to resolve further
      return filePath;
    }
  }
  ['.', __dirname].forEach(function (base) {
    possiblePath = path.resolve(base, filePath);
    if (fs.existsSync(possiblePath)) {
      return possiblePath;
    }
  });
  return null;
}

function makeCertFileCoercer(type, description, helpText) {
  return function certFileCoercer(value) {
    if (matchesCertType(value, type)) {
      return value;
    }

    const filePath = resolveFilePath(value);
    if (filePath) {
      return fs.readFileSync(filePath, 'utf8')
    }
    throw new Error(
      'Invalid ' + description + ', not a valid cert/key or file path' +
      (helpText ? '\n' + helpText : '')
    )
  };
}

function certToPEM(cert) {
  if (/-----BEGIN CERTIFICATE-----/.test(cert)) {
    return cert;
  }

  cert = cert.match(/.{1,64}/g).join('\n');
  cert = "-----BEGIN CERTIFICATE-----\n" + cert;
  cert = cert + "\n-----END CERTIFICATE-----\n";
  return cert;
}

function processArgs(options) {
  var baseArgv;
  console.log();
  console.log('parsing arguments...');
  if (options) {
    baseArgv = yargs.config(options);
  } else {
    baseArgv = yargs.config('settings', function(settingsPathArg) {
      const settingsPath = resolveFilePath(settingsPathArg);
      return JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
    });
  }
  return baseArgv
    .usage('\nSimple SAML Service Provider / WS-Federation Relying Party\n\n' +
      'Usage:  $0 <options>')
    .options({
      port: {
        alias: 'p',
        description: 'Web Server listener port',
        required: true,
        number: true,
        default: 7070
      },
      protocol: {
        description: 'Federation Protocol',
        required: true,
        string: true,
        default: 'samlp'
      },
      idpIssuer: {
        alias: 'iss',
        description: 'IdP Issuer URI',
        required: false,
        string: true,
        default: 'urn:example:idp'
      },
      idpSsoUrl: {
        description: 'IdP Single Sign-On Service URL (SSO URL)',
        required: false,
        string: true
      },
      idpSsoBinding: {
        description: 'IdP Single Sign-On AuthnRequest Binding',
        required: true,
        string: true,
        default: BINDINGS.REDIRECT,
        choices: [BINDINGS.REDIRECT, BINDINGS.POST]
      },
      idpSloUrl: {
        description: 'IdP Single Logout Service URL (SLO URL) (SAMLP)',
        required: false,
        string: true
      },
      idpSloBinding: {
        description: 'IdP Single Logout Request Binding (SAMLP)',
        required: true,
        string: true,
        default: BINDINGS.REDIRECT,
        choices: [BINDINGS.REDIRECT, BINDINGS.POST]
      },
      idpCert: {
        description: 'IdP Public Key Signing Certificate (PEM)',
        required: false,
        string: true,
        coerce: (value) => {
          return certToPEM(makeCertFileCoercer('certificate', 'IdP Public Key Signing Certificate (PEM)', KEY_CERT_HELP_TEXT));
        }
      },
      idpThumbprint: {
        description: 'IdP Public Key Signing Certificate SHA1 Thumbprint',
        required: false,
        string: true,
        coerce: (value) => {
          return value ? value.replace(/:/g, '') : value
        }
      },
      idpMetaUrl: {
        description: 'IdP SAML Metadata URL',
        required: false,<commit_msg>fixed cert.match is not function error when passing filepath for idpCert<commit_after>'use strict';

const _                   = require('underscore'),
      os                  = require('os'),
      fs                  = require('fs'),
      http                = require('http'),
      https               = require('https'),
      path                = require('path'),
      yargs               = require('yargs'),
      IdPMetadata        = require('./idp-metadata'),
      App                 = require('../app/app');

/**
 * Globals
 */

const NAMEID_FORMAT_PREFERENCE = [
  'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
  'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
  'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
  'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
  'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos',
  'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName'
]

const BINDINGS = {
  REDIRECT: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
  POST: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
}

const KEY_CERT_HELP_TEXT = "Please generate a key-pair for the SP using the following openssl command:\n" +
      "\topenssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Service Provider' -keyout ./config/sp-key.pem -out ./config/sp-cert.pem -days 7300";

const PEM_TYPES = {
  certificate: /-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----/,
  privateKey: /-----BEGIN RSA PRIVATE KEY-----\n[^-]*\n-----END RSA PRIVATE KEY-----/,
  publicKey: /-----BEGIN PUBLIC KEY-----\n[^-]*\n-----END PUBLIC KEY-----/,
};

function matchesCertType(value, type) {
  // console.info(`Testing ${PEM_TYPES[type].toString()} against "${value}"`);
  // console.info(`result: ${PEM_TYPES[type] && PEM_TYPES[type].test(value)}`);
  return PEM_TYPES[type] && PEM_TYPES[type].test(value);
}

function bufferFromString(value) {
  if (Buffer.hasOwnProperty('from')) {
    // node 6+
    return Buffer.from(value);
  } else {
    return new Buffer(value);
  }
}

function resolveFilePath(filePath) {
  var possiblePath;
  console.log('resolving path %s', filePath);
  if (fs.existsSync(filePath)) {
    return filePath;
  }
  if (filePath.slice(0, 2) === '~/') {
    possiblePath = path.resolve(process.env.HOME, filePath.slice(2));
    if (fs.existsSync(possiblePath)) {
      return possiblePath;
    } else {
      // for ~/ paths, don't try to resolve further
      return filePath;
    }
  }
  ['.', __dirname].forEach(function (base) {
    possiblePath = path.resolve(base, filePath);
    if (fs.existsSync(possiblePath)) {
      return possiblePath;
    }
  });
  return null;
}

function makeCertFileCoercer(type, description, helpText) {
  return function certFileCoercer(value) {
    if (matchesCertType(value, type)) {
      return value;
    }

    const filePath = resolveFilePath(value);
    if (filePath) {
      return fs.readFileSync(filePath, 'utf8')
    }
    throw new Error(
      'Invalid ' + description + ', not a valid cert/key or file path' +
      (helpText ? '\n' + helpText : '')
    )
  };
}

function certToPEM(cert) {
  if (/-----BEGIN CERTIFICATE-----/.test(cert)) {
    return cert;
  }

  cert = cert.match(/.{1,64}/g).join('\n');
  cert = "-----BEGIN CERTIFICATE-----\n" + cert;
  cert = cert + "\n-----END CERTIFICATE-----\n";
  return cert;
}

function processArgs(options) {
  var baseArgv;
  console.log();
  console.log('parsing arguments...');
  if (options) {
    baseArgv = yargs.config(options);
  } else {
    baseArgv = yargs.config('settings', function(settingsPathArg) {
      const settingsPath = resolveFilePath(settingsPathArg);
      return JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
    });
  }
  return baseArgv
    .usage('\nSimple SAML Service Provider / WS-Federation Relying Party\n\n' +
      'Usage:  $0 <options>')
    .options({
      port: {
        alias: 'p',
        description: 'Web Server listener port',
        required: true,
        number: true,
        default: 7070
      },
      protocol: {
        description: 'Federation Protocol',
        required: true,
        string: true,
        default: 'samlp'
      },
      idpIssuer: {
        alias: 'iss',
        description: 'IdP Issuer URI',
        required: false,
        string: true,
        default: 'urn:example:idp'
      },
      idpSsoUrl: {
        description: 'IdP Single Sign-On Service URL (SSO URL)',
        required: false,
        string: true
      },
      idpSsoBinding: {
        description: 'IdP Single Sign-On AuthnRequest Binding',
        required: true,
        string: true,
        default: BINDINGS.REDIRECT,
        choices: [BINDINGS.REDIRECT, BINDINGS.POST]
      },
      idpSloUrl: {
        description: 'IdP Single Logout Service URL (SLO URL) (SAMLP)',
        required: false,
        string: true
      },
      idpSloBinding: {
        description: 'IdP Single Logout Request Binding (SAMLP)',
        required: true,
        string: true,
        default: BINDINGS.REDIRECT,
        choices: [BINDINGS.REDIRECT, BINDINGS.POST]
      },
      idpCert: {
        description: 'IdP Public Key Signing Certificate (PEM)',
        required: false,
        string: true,
        coerce: (value) => {
          return certToPEM(makeCertFileCoercer('certificate', 'IdP Public Key Signing Certificate (PEM)', KEY_CERT_HELP_TEXT)(value));
        }
      },
      idpThumbprint: {
        description: 'IdP Public Key Signing Certificate SHA1 Thumbprint',
        required: false,
        string: true,
        coerce: (value) => {
          return value ? value.replace(/:/g, '') : value
        }
      },
      idpMetaUrl: {
        description: 'IdP SAML Metadata URL',
        required: false,
{'fraction_non_alphanumeric': 0.15450606585788562, 'fraction_numerical': 0.007279029462738302, 'mean_word_length': 2.4657657657657657, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, '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': '5577249', 'n_tokens_mistral': 3900, 'n_tokens_neox': 3783, 'n_words': 789}