tern-tools / tern

Tern is a software composition analysis tool and Python library that generates a Software Bill of Materials for container images and Dockerfiles. The SBOM that Tern generates will give you a layer-by-layer view of what's inside your container in a variety of formats including human-readable, JSON, HTML, SPDX and more.
BSD 2-Clause "Simplified" License
962 stars 188 forks source link

Replace npm parsing with jq #903

Closed JamieMagee closed 3 years ago

JamieMagee commented 3 years ago

Describe the Feature The current implementation for parsing npm packages is rather slow. This is due to using npm directly, which has the overhead of spinning up Node.

Instead, Tern should get the complete output using npm list -g --json --long --depth=1000 and parse that output using jq.

Use Cases In tests, run by @jcfiorenzano, we saw runtimes of 23-25 minutes for the following Dockerfile

FROM node:12.16-alpine
RUN npm install -g serve 

Implementation Changes

The current commands used to scan npm here:

https://github.com/tern-tools/tern/blob/66822ef16cb09d45db929b453a5d9f04d0a5e838/tern/analyze/default/command_lib/base.yml#L384-L410

Should be replaced by equivalent jq commands. A sample jq command is

jq -c '.. | objects | select(has("name") and has("version")) | {name: .name, version: .version, license: .license}'
Output
```json {"name":"npm","version":"7.6.0","license":"Artistic-2.0"} {"name":"@npmcli/arborist","version":"2.2.5","license":"ISC"} {"name":"@npmcli/installed-package-contents","version":"1.0.7","license":"ISC"} {"name":"npm-bundled","version":"1.1.1","license":"ISC"} {"name":"npm-normalize-package-bin","version":"1.0.1","license":"ISC"} {"name":"npm-normalize-package-bin","version":"1.0.1","license":"ISC"} {"name":"@npmcli/map-workspaces","version":"1.0.3","license":"ISC"} {"name":"@npmcli/name-from-folder","version":"1.0.1","license":"ISC"} {"name":"glob","version":"7.1.6","license":"ISC"} {"name":"minimatch","version":"3.0.4","license":"ISC"} {"name":"read-package-json-fast","version":"2.0.2","license":"ISC"} {"name":"@npmcli/metavuln-calculator","version":"1.1.0","license":"ISC"} {"name":"cacache","version":"15.0.5","license":"ISC"} {"name":"pacote","version":"11.2.7","license":"ISC"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"@npmcli/move-file","version":"1.1.2","license":"MIT"} {"name":"mkdirp","version":"1.0.4","license":"MIT"} {"name":"rimraf","version":"3.0.2","license":"ISC"} {"name":"@npmcli/name-from-folder","version":"1.0.1","license":"ISC"} {"name":"@npmcli/node-gyp","version":"1.0.2","license":"ISC"} {"name":"@npmcli/run-script","version":"1.8.3","license":"ISC"} {"name":"bin-links","version":"2.2.1","license":"ISC"} {"name":"cmd-shim","version":"4.1.0","license":"ISC"} {"name":"mkdirp-infer-owner","version":"2.0.0","license":"ISC"} {"name":"mkdirp","version":"1.0.4","license":"MIT"} {"name":"npm-normalize-package-bin","version":"1.0.1","license":"ISC"} {"name":"read-cmd-shim","version":"2.0.0","license":"ISC"} {"name":"rimraf","version":"3.0.2","license":"ISC"} {"name":"write-file-atomic","version":"3.0.3","license":"ISC"} {"name":"cacache","version":"15.0.5","license":"ISC"} {"name":"common-ancestor-path","version":"1.0.1","license":"ISC"} {"name":"json-parse-even-better-errors","version":"2.3.1","license":"MIT"} {"name":"json-stringify-nice","version":"1.1.1","license":"ISC"} {"name":"mkdirp-infer-owner","version":"2.0.0","license":"ISC"} {"name":"npm-install-checks","version":"4.0.0","license":"BSD-2-Clause"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"npm-package-arg","version":"8.1.1","license":"ISC"} {"name":"npm-pick-manifest","version":"6.1.0","license":"ISC"} {"name":"npm-registry-fetch","version":"9.0.0","license":"ISC"} {"name":"pacote","version":"11.2.7","license":"ISC"} {"name":"parse-conflict-json","version":"1.1.1","license":"ISC"} {"name":"promise-all-reject-late","version":"1.0.1","license":"ISC"} {"name":"promise-call-limit","version":"1.0.1","license":"ISC"} {"name":"read-package-json-fast","version":"2.0.2","license":"ISC"} {"name":"readdir-scoped-modules","version":"1.1.0","license":"ISC"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"tar","version":"6.1.0","license":"ISC"} {"name":"treeverse","version":"1.0.4","license":"ISC"} {"name":"walk-up-path","version":"1.0.0","license":"ISC"} {"name":"@npmcli/ci-detect","version":"1.3.0","license":"ISC"} {"name":"@npmcli/config","version":"1.2.9","license":"ISC"} {"name":"ini","version":"2.0.0","license":"ISC"} {"name":"mkdirp-infer-owner","version":"2.0.0","license":"ISC"} {"name":"nopt","version":"5.0.0","license":"ISC"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"walk-up-path","version":"1.0.0","license":"ISC"} {"name":"@npmcli/run-script","version":"1.8.3","license":"ISC"} {"name":"@npmcli/node-gyp","version":"1.0.2","license":"ISC"} {"name":"@npmcli/promise-spawn","version":"1.3.2","license":"ISC"} {"name":"infer-owner","version":"1.0.4","license":"ISC"} {"name":"infer-owner","version":"1.0.4","license":"ISC"} {"name":"node-gyp","version":"7.1.2","license":"MIT"} {"name":"puka","version":"1.0.1","license":"MIT"} {"name":"read-package-json-fast","version":"2.0.2","license":"ISC"} {"name":"abbrev","version":"1.1.1","license":"ISC"} {"name":"ansicolors","version":"0.3.2","license":"MIT"} {"name":"ansistyles","version":"0.1.3","license":"MIT"} {"name":"archy","version":"1.0.0","license":"MIT"} {"name":"byte-size","version":"7.0.0","license":"MIT"} {"name":"cacache","version":"15.0.5","license":"ISC"} {"name":"@npmcli/move-file","version":"1.1.2","license":"MIT"} {"name":"chownr","version":"2.0.0","license":"ISC"} {"name":"fs-minipass","version":"2.1.0","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"glob","version":"7.1.6","license":"ISC"} {"name":"infer-owner","version":"1.0.4","license":"ISC"} {"name":"lru-cache","version":"6.0.0","license":"ISC"} {"name":"yallist","version":"4.0.0","license":"ISC"} {"name":"minipass-collect","version":"1.0.2","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"minipass-flush","version":"1.0.5","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"minipass-pipeline","version":"1.2.4","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"mkdirp","version":"1.0.4","license":"MIT"} {"name":"p-map","version":"4.0.0","license":"MIT"} {"name":"aggregate-error","version":"3.1.0","license":"MIT"} {"name":"clean-stack","version":"2.2.0","license":"MIT"} {"name":"indent-string","version":"4.0.0","license":"MIT"} {"name":"promise-inflight","version":"1.0.1","license":"ISC"} {"name":"rimraf","version":"3.0.2","license":"ISC"} {"name":"ssri","version":"8.0.1","license":"ISC"} {"name":"tar","version":"6.1.0","license":"ISC"} {"name":"unique-filename","version":"1.1.1","license":"ISC"} {"name":"unique-slug","version":"2.0.2","license":"ISC"} {"name":"imurmurhash","version":"0.1.4","license":"MIT"} {"name":"chalk","version":"4.1.0","license":"MIT"} {"name":"ansi-styles","version":"4.3.0","license":"MIT"} {"name":"color-convert","version":"2.0.1","license":"MIT"} {"name":"color-name","version":"1.1.4","license":"MIT"} {"name":"supports-color","version":"7.2.0","license":"MIT"} {"name":"has-flag","version":"4.0.0","license":"MIT"} {"name":"chownr","version":"2.0.0","license":"ISC"} {"name":"cli-columns","version":"3.1.2","license":"MIT"} {"name":"string-width","version":"2.1.1","license":"MIT"} {"name":"is-fullwidth-code-point","version":"2.0.0","license":"MIT"} {"name":"strip-ansi","version":"4.0.0","license":"MIT"} {"name":"ansi-regex","version":"3.0.0","license":"MIT"} {"name":"strip-ansi","version":"3.0.1","license":"MIT"} {"name":"ansi-regex","version":"2.1.1","license":"MIT"} {"name":"cli-table3","version":"0.6.0","license":"MIT"} {"name":"colors","version":"1.4.0","license":"MIT"} {"name":"object-assign","version":"4.1.1","license":"MIT"} {"name":"string-width","version":"4.2.0","license":"MIT"} {"name":"emoji-regex","version":"8.0.0","license":"MIT"} {"name":"is-fullwidth-code-point","version":"3.0.0","license":"MIT"} {"name":"strip-ansi","version":"6.0.0","license":"MIT"} {"name":"ansi-regex","version":"5.0.0","license":"MIT"} {"name":"columnify","version":"1.5.4","license":"MIT"} {"name":"strip-ansi","version":"3.0.1","license":"MIT"} {"name":"wcwidth","version":"1.0.1","license":"MIT"} {"name":"defaults","version":"1.0.3","license":"MIT"} {"name":"clone","version":"1.0.4","license":"MIT"} {"name":"glob","version":"7.1.6","license":"ISC"} {"name":"fs.realpath","version":"1.0.0","license":"ISC"} {"name":"inflight","version":"1.0.6","license":"ISC"} {"name":"once","version":"1.4.0","license":"ISC"} {"name":"wrappy","version":"1.0.2","license":"ISC"} {"name":"inherits","version":"2.0.4","license":"ISC"} {"name":"minimatch","version":"3.0.4","license":"ISC"} {"name":"brace-expansion","version":"1.1.11","license":"MIT"} {"name":"balanced-match","version":"1.0.0","license":"MIT"} {"name":"concat-map","version":"0.0.1","license":"MIT"} {"name":"once","version":"1.4.0","license":"ISC"} {"name":"wrappy","version":"1.0.2","license":"ISC"} {"name":"path-is-absolute","version":"1.0.1","license":"MIT"} {"name":"graceful-fs","version":"4.2.6","license":"ISC"} {"name":"hosted-git-info","version":"3.0.8","license":"ISC"} {"name":"lru-cache","version":"6.0.0","license":"ISC"} {"name":"ini","version":"2.0.0","license":"ISC"} {"name":"init-package-json","version":"2.0.2","license":"ISC"} {"name":"glob","version":"7.1.6","license":"ISC"} {"name":"npm-package-arg","version":"8.1.1","license":"ISC"} {"name":"promzard","version":"0.3.0","license":"ISC"} {"name":"read","version":"1.0.7","license":"ISC"} {"name":"read-package-json","version":"3.0.1","license":"ISC"} {"name":"read","version":"1.0.7","license":"ISC"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"validate-npm-package-license","version":"3.0.4","license":"Apache-2.0"} {"name":"spdx-correct","version":"3.1.1","license":"Apache-2.0"} {"name":"spdx-expression-parse","version":"3.0.1","license":"MIT"} {"name":"spdx-license-ids","version":"3.0.7","license":"CC0-1.0"} {"name":"spdx-expression-parse","version":"3.0.1","license":"MIT"} {"name":"spdx-exceptions","version":"2.3.0","license":"CC-BY-3.0"} {"name":"spdx-license-ids","version":"3.0.7","license":"CC0-1.0"} {"name":"validate-npm-package-name","version":"3.0.0","license":"ISC"} {"name":"is-cidr","version":"4.0.2","license":"BSD-2-Clause"} {"name":"cidr-regex","version":"3.1.1","license":"BSD-2-Clause"} {"name":"ip-regex","version":"4.3.0","license":"MIT"} {"name":"json-parse-even-better-errors","version":"2.3.1","license":"MIT"} {"name":"leven","version":"3.1.0","license":"MIT"} {"name":"libnpmaccess","version":"4.0.1","license":"ISC"} {"name":"aproba","version":"2.0.0","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"npm-package-arg","version":"8.1.1","license":"ISC"} {"name":"npm-registry-fetch","version":"9.0.0","license":"ISC"} {"name":"libnpmdiff","version":"2.0.3","license":"ISC"} {"name":"@npmcli/disparity-colors","version":"1.0.1","license":"ISC"} {"name":"ansi-styles","version":"4.3.0","license":"MIT"} {"name":"binary-extensions","version":"2.2.0","license":"MIT"} {"name":"diff","version":"5.0.0","license":"BSD-3-Clause"} {"name":"minimatch","version":"3.0.4","license":"ISC"} {"name":"pacote","version":"11.2.7","license":"ISC"} {"name":"tar","version":"6.1.0","license":"ISC"} {"name":"libnpmfund","version":"1.0.2","license":"ISC"} {"name":"@npmcli/arborist","version":"2.2.5","license":"ISC"} {"name":"libnpmhook","version":"6.0.1","license":"ISC"} {"name":"aproba","version":"2.0.0","license":"ISC"} {"name":"npm-registry-fetch","version":"9.0.0","license":"ISC"} {"name":"libnpmorg","version":"2.0.1","license":"ISC"} {"name":"aproba","version":"2.0.0","license":"ISC"} {"name":"npm-registry-fetch","version":"9.0.0","license":"ISC"} {"name":"libnpmpack","version":"2.0.1","license":"ISC"} {"name":"@npmcli/run-script","version":"1.8.3","license":"ISC"} {"name":"npm-package-arg","version":"8.1.1","license":"ISC"} {"name":"pacote","version":"11.2.7","license":"ISC"} {"name":"libnpmpublish","version":"4.0.0","license":"ISC"} {"name":"normalize-package-data","version":"3.0.0","license":"BSD-2-Clause"} {"name":"hosted-git-info","version":"3.0.8","license":"ISC"} {"name":"resolve","version":"1.20.0","license":"MIT"} {"name":"is-core-module","version":"2.2.0","license":"MIT"} {"name":"has","version":"1.0.3","license":"MIT"} {"name":"function-bind","version":"1.1.1","license":"MIT"} {"name":"path-parse","version":"1.0.6","license":"MIT"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"validate-npm-package-license","version":"3.0.4","license":"Apache-2.0"} {"name":"npm-package-arg","version":"8.1.1","license":"ISC"} {"name":"npm-registry-fetch","version":"9.0.0","license":"ISC"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"ssri","version":"8.0.1","license":"ISC"} {"name":"libnpmsearch","version":"3.1.0","license":"ISC"} {"name":"npm-registry-fetch","version":"9.0.0","license":"ISC"} {"name":"libnpmteam","version":"2.0.2","license":"ISC"} {"name":"aproba","version":"2.0.0","license":"ISC"} {"name":"npm-registry-fetch","version":"9.0.0","license":"ISC"} {"name":"libnpmversion","version":"1.0.11","license":"ISC"} {"name":"@npmcli/git","version":"2.0.6","license":"ISC"} {"name":"@npmcli/promise-spawn","version":"1.3.2","license":"ISC"} {"name":"lru-cache","version":"6.0.0","license":"ISC"} {"name":"mkdirp","version":"1.0.4","license":"MIT"} {"name":"npm-pick-manifest","version":"6.1.0","license":"ISC"} {"name":"promise-inflight","version":"1.0.1","license":"ISC"} {"name":"promise-retry","version":"2.0.1","license":"MIT"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"unique-filename","version":"1.1.1","license":"ISC"} {"name":"which","version":"2.0.2","license":"ISC"} {"name":"@npmcli/run-script","version":"1.8.3","license":"ISC"} {"name":"read-package-json-fast","version":"2.0.2","license":"ISC"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"stringify-package","version":"1.0.1","license":"ISC"} {"name":"make-fetch-happen","version":"8.0.14","license":"ISC"} {"name":"agentkeepalive","version":"4.1.4","license":"MIT"} {"name":"debug","version":"4.3.1","license":"MIT"} {"name":"ms","version":"2.1.2","license":"MIT"} {"name":"depd","version":"1.1.2","license":"MIT"} {"name":"humanize-ms","version":"1.2.1","license":"MIT"} {"name":"ms","version":"2.1.3","license":"MIT"} {"name":"cacache","version":"15.0.5","license":"ISC"} {"name":"http-cache-semantics","version":"4.1.0","license":"BSD-2-Clause"} {"name":"http-proxy-agent","version":"4.0.1","license":"MIT"} {"name":"@tootallnate/once","version":"1.1.2","license":"MIT"} {"name":"agent-base","version":"6.0.2","license":"MIT"} {"name":"debug","version":"4.3.1","license":"MIT"} {"name":"debug","version":"4.3.1","license":"MIT"} {"name":"https-proxy-agent","version":"5.0.0","license":"MIT"} {"name":"agent-base","version":"6.0.2","license":"MIT"} {"name":"debug","version":"4.3.1","license":"MIT"} {"name":"is-lambda","version":"1.0.1","license":"MIT"} {"name":"lru-cache","version":"6.0.0","license":"ISC"} {"name":"minipass-collect","version":"1.0.2","license":"ISC"} {"name":"minipass-fetch","version":"1.3.3","license":"MIT"} {"name":"encoding","version":"0.1.13","license":"MIT"} {"name":"iconv-lite","version":"0.6.2","license":"MIT"} {"name":"safer-buffer","version":"2.1.2","license":"MIT"} {"name":"minipass-sized","version":"1.0.3","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"minizlib","version":"2.1.2","license":"MIT"} {"name":"minipass-flush","version":"1.0.5","license":"ISC"} {"name":"minipass-pipeline","version":"1.2.4","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"promise-retry","version":"2.0.1","license":"MIT"} {"name":"err-code","version":"2.0.3","license":"MIT"} {"name":"retry","version":"0.12.0","license":"MIT"} {"name":"socks-proxy-agent","version":"5.0.0","license":"MIT"} {"name":"agent-base","version":"6.0.2","license":"MIT"} {"name":"debug","version":"4.3.1","license":"MIT"} {"name":"socks","version":"2.5.1","license":"MIT"} {"name":"ip","version":"1.1.5","license":"MIT"} {"name":"smart-buffer","version":"4.1.0","license":"MIT"} {"name":"ssri","version":"8.0.1","license":"ISC"} {"name":"minipass-pipeline","version":"1.2.4","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"yallist","version":"4.0.0","license":"ISC"} {"name":"mkdirp-infer-owner","version":"2.0.0","license":"ISC"} {"name":"chownr","version":"2.0.0","license":"ISC"} {"name":"infer-owner","version":"1.0.4","license":"ISC"} {"name":"mkdirp","version":"1.0.4","license":"MIT"} {"name":"mkdirp","version":"1.0.4","license":"MIT"} {"name":"ms","version":"2.1.3","license":"MIT"} {"name":"node-gyp","version":"7.1.2","license":"MIT"} {"name":"env-paths","version":"2.2.0","license":"MIT"} {"name":"glob","version":"7.1.6","license":"ISC"} {"name":"graceful-fs","version":"4.2.6","license":"ISC"} {"name":"nopt","version":"5.0.0","license":"ISC"} {"name":"npmlog","version":"4.1.2","license":"ISC"} {"name":"request","version":"2.88.2","license":"Apache-2.0"} {"name":"aws-sign2","version":"0.7.0","license":"Apache-2.0"} {"name":"aws4","version":"1.11.0","license":"MIT"} {"name":"caseless","version":"0.12.0","license":"Apache-2.0"} {"name":"combined-stream","version":"1.0.8","license":"MIT"} {"name":"delayed-stream","version":"1.0.0","license":"MIT"} {"name":"extend","version":"3.0.2","license":"MIT"} {"name":"forever-agent","version":"0.6.1","license":"Apache-2.0"} {"name":"form-data","version":"2.3.3","license":"MIT"} {"name":"asynckit","version":"0.4.0","license":"MIT"} {"name":"combined-stream","version":"1.0.8","license":"MIT"} {"name":"mime-types","version":"2.1.28","license":"MIT"} {"name":"har-validator","version":"5.1.5","license":"MIT"} {"name":"ajv","version":"6.12.6","license":"MIT"} {"name":"fast-deep-equal","version":"3.1.3","license":"MIT"} {"name":"fast-json-stable-stringify","version":"2.1.0","license":"MIT"} {"name":"json-schema-traverse","version":"0.4.1","license":"MIT"} {"name":"uri-js","version":"4.4.1","license":"BSD-2-Clause"} {"name":"punycode","version":"2.1.1","license":"MIT"} {"name":"har-schema","version":"2.0.0","license":"ISC"} {"name":"http-signature","version":"1.2.0","license":"MIT"} {"name":"assert-plus","version":"1.0.0","license":"MIT"} {"name":"jsprim","version":"1.4.1","license":"MIT"} {"name":"assert-plus","version":"1.0.0","license":"MIT"} {"name":"extsprintf","version":"1.3.0","license":"MIT"} {"name":"json-schema","version":"0.2.3","license":null} {"name":"verror","version":"1.10.0","license":"MIT"} {"name":"assert-plus","version":"1.0.0","license":"MIT"} {"name":"core-util-is","version":"1.0.2","license":"MIT"} {"name":"extsprintf","version":"1.3.0","license":"MIT"} {"name":"sshpk","version":"1.16.1","license":"MIT"} {"name":"asn1","version":"0.2.4","license":"MIT"} {"name":"safer-buffer","version":"2.1.2","license":"MIT"} {"name":"assert-plus","version":"1.0.0","license":"MIT"} {"name":"bcrypt-pbkdf","version":"1.0.2","license":"BSD-3-Clause"} {"name":"tweetnacl","version":"0.14.5","license":"Unlicense"} {"name":"dashdash","version":"1.14.1","license":"MIT"} {"name":"assert-plus","version":"1.0.0","license":"MIT"} {"name":"ecc-jsbn","version":"0.1.2","license":"MIT"} {"name":"jsbn","version":"0.1.1","license":"MIT"} {"name":"safer-buffer","version":"2.1.2","license":"MIT"} {"name":"getpass","version":"0.1.7","license":"MIT"} {"name":"assert-plus","version":"1.0.0","license":"MIT"} {"name":"jsbn","version":"0.1.1","license":"MIT"} {"name":"safer-buffer","version":"2.1.2","license":"MIT"} {"name":"tweetnacl","version":"0.14.5","license":"Unlicense"} {"name":"is-typedarray","version":"1.0.0","license":"MIT"} {"name":"isstream","version":"0.1.2","license":"MIT"} {"name":"json-stringify-safe","version":"5.0.1","license":"ISC"} {"name":"mime-types","version":"2.1.28","license":"MIT"} {"name":"mime-db","version":"1.45.0","license":"MIT"} {"name":"oauth-sign","version":"0.9.0","license":"Apache-2.0"} {"name":"performance-now","version":"2.1.0","license":"MIT"} {"name":"qs","version":"6.5.2","license":"BSD-3-Clause"} {"name":"safe-buffer","version":"5.1.2","license":"MIT"} {"name":"tough-cookie","version":"2.5.0","license":"BSD-3-Clause"} {"name":"psl","version":"1.8.0","license":"MIT"} {"name":"punycode","version":"2.1.1","license":"MIT"} {"name":"tunnel-agent","version":"0.6.0","license":"Apache-2.0"} {"name":"safe-buffer","version":"5.1.2","license":"MIT"} {"name":"uuid","version":"3.4.0","license":"MIT"} {"name":"rimraf","version":"3.0.2","license":"ISC"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"tar","version":"6.1.0","license":"ISC"} {"name":"which","version":"2.0.2","license":"ISC"} {"name":"nopt","version":"5.0.0","license":"ISC"} {"name":"abbrev","version":"1.1.1","license":"ISC"} {"name":"npm-audit-report","version":"2.1.4","license":"ISC"} {"name":"chalk","version":"4.1.0","license":"MIT"} {"name":"npm-package-arg","version":"8.1.1","license":"ISC"} {"name":"hosted-git-info","version":"3.0.8","license":"ISC"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"validate-npm-package-name","version":"3.0.0","license":"ISC"} {"name":"npm-pick-manifest","version":"6.1.0","license":"ISC"} {"name":"npm-install-checks","version":"4.0.0","license":"BSD-2-Clause"} {"name":"npm-package-arg","version":"8.1.1","license":"ISC"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"npm-profile","version":"5.0.2","license":"ISC"} {"name":"npm-registry-fetch","version":"9.0.0","license":"ISC"} {"name":"npm-registry-fetch","version":"9.0.0","license":"ISC"} {"name":"@npmcli/ci-detect","version":"1.3.0","license":"ISC"} {"name":"lru-cache","version":"6.0.0","license":"ISC"} {"name":"make-fetch-happen","version":"8.0.14","license":"ISC"} {"name":"minipass-fetch","version":"1.3.3","license":"MIT"} {"name":"minipass-json-stream","version":"1.0.1","license":"MIT"} {"name":"jsonparse","version":"1.3.1","license":"MIT"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"minizlib","version":"2.1.2","license":"MIT"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"yallist","version":"4.0.0","license":"ISC"} {"name":"npm-package-arg","version":"8.1.1","license":"ISC"} {"name":"npm-user-validate","version":"1.0.1","license":"BSD-2-Clause"} {"name":"npmlog","version":"4.1.2","license":"ISC"} {"name":"are-we-there-yet","version":"1.1.5","license":"ISC"} {"name":"delegates","version":"1.0.0","license":"MIT"} {"name":"readable-stream","version":"2.3.7","license":"MIT"} {"name":"core-util-is","version":"1.0.2","license":"MIT"} {"name":"inherits","version":"2.0.4","license":"ISC"} {"name":"isarray","version":"1.0.0","license":"MIT"} {"name":"process-nextick-args","version":"2.0.1","license":"MIT"} {"name":"safe-buffer","version":"5.1.2","license":"MIT"} {"name":"string_decoder","version":"1.1.1","license":"MIT"} {"name":"safe-buffer","version":"5.1.2","license":"MIT"} {"name":"util-deprecate","version":"1.0.2","license":"MIT"} {"name":"console-control-strings","version":"1.1.0","license":"ISC"} {"name":"gauge","version":"2.7.4","license":"ISC"} {"name":"aproba","version":"1.2.0","license":"ISC"} {"name":"console-control-strings","version":"1.1.0","license":"ISC"} {"name":"has-unicode","version":"2.0.1","license":"ISC"} {"name":"object-assign","version":"4.1.1","license":"MIT"} {"name":"signal-exit","version":"3.0.3","license":"ISC"} {"name":"string-width","version":"1.0.2","license":"MIT"} {"name":"code-point-at","version":"1.1.0","license":"MIT"} {"name":"is-fullwidth-code-point","version":"1.0.0","license":"MIT"} {"name":"number-is-nan","version":"1.0.1","license":"MIT"} {"name":"strip-ansi","version":"3.0.1","license":"MIT"} {"name":"strip-ansi","version":"3.0.1","license":"MIT"} {"name":"wide-align","version":"1.1.3","license":"ISC"} {"name":"string-width","version":"2.1.1","license":"MIT"} {"name":"set-blocking","version":"2.0.0","license":"ISC"} {"name":"opener","version":"1.5.2","license":"(WTFPL OR MIT)"} {"name":"pacote","version":"11.2.7","license":"ISC"} {"name":"@npmcli/git","version":"2.0.6","license":"ISC"} {"name":"@npmcli/installed-package-contents","version":"1.0.7","license":"ISC"} {"name":"@npmcli/promise-spawn","version":"1.3.2","license":"ISC"} {"name":"@npmcli/run-script","version":"1.8.3","license":"ISC"} {"name":"cacache","version":"15.0.5","license":"ISC"} {"name":"chownr","version":"2.0.0","license":"ISC"} {"name":"fs-minipass","version":"2.1.0","license":"ISC"} {"name":"infer-owner","version":"1.0.4","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"mkdirp","version":"1.0.4","license":"MIT"} {"name":"npm-package-arg","version":"8.1.1","license":"ISC"} {"name":"npm-packlist","version":"2.1.4","license":"ISC"} {"name":"glob","version":"7.1.6","license":"ISC"} {"name":"ignore-walk","version":"3.0.3","license":"ISC"} {"name":"minimatch","version":"3.0.4","license":"ISC"} {"name":"npm-bundled","version":"1.1.1","license":"ISC"} {"name":"npm-normalize-package-bin","version":"1.0.1","license":"ISC"} {"name":"npm-pick-manifest","version":"6.1.0","license":"ISC"} {"name":"npm-registry-fetch","version":"9.0.0","license":"ISC"} {"name":"promise-retry","version":"2.0.1","license":"MIT"} {"name":"read-package-json-fast","version":"2.0.2","license":"ISC"} {"name":"rimraf","version":"3.0.2","license":"ISC"} {"name":"ssri","version":"8.0.1","license":"ISC"} {"name":"tar","version":"6.1.0","license":"ISC"} {"name":"parse-conflict-json","version":"1.1.1","license":"ISC"} {"name":"json-parse-even-better-errors","version":"2.3.1","license":"MIT"} {"name":"just-diff-apply","version":"3.0.0","license":"MIT"} {"name":"just-diff","version":"3.0.2","license":"MIT"} {"name":"qrcode-terminal","version":"0.12.0","license":null} {"name":"read-package-json-fast","version":"2.0.2","license":"ISC"} {"name":"json-parse-even-better-errors","version":"2.3.1","license":"MIT"} {"name":"npm-normalize-package-bin","version":"1.0.1","license":"ISC"} {"name":"read-package-json","version":"3.0.1","license":"ISC"} {"name":"glob","version":"7.1.6","license":"ISC"} {"name":"json-parse-even-better-errors","version":"2.3.1","license":"MIT"} {"name":"normalize-package-data","version":"3.0.0","license":"BSD-2-Clause"} {"name":"npm-normalize-package-bin","version":"1.0.1","license":"ISC"} {"name":"read","version":"1.0.7","license":"ISC"} {"name":"mute-stream","version":"0.0.8","license":"ISC"} {"name":"readdir-scoped-modules","version":"1.1.0","license":"ISC"} {"name":"debuglog","version":"1.0.1","license":"MIT"} {"name":"dezalgo","version":"1.0.3","license":"ISC"} {"name":"asap","version":"2.0.6","license":"MIT"} {"name":"wrappy","version":"1.0.2","license":"ISC"} {"name":"graceful-fs","version":"4.2.6","license":"ISC"} {"name":"once","version":"1.4.0","license":"ISC"} {"name":"rimraf","version":"3.0.2","license":"ISC"} {"name":"glob","version":"7.1.6","license":"ISC"} {"name":"semver","version":"7.3.4","license":"ISC"} {"name":"lru-cache","version":"6.0.0","license":"ISC"} {"name":"ssri","version":"8.0.1","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"tar","version":"6.1.0","license":"ISC"} {"name":"chownr","version":"2.0.0","license":"ISC"} {"name":"fs-minipass","version":"2.1.0","license":"ISC"} {"name":"minipass","version":"3.1.3","license":"ISC"} {"name":"minizlib","version":"2.1.2","license":"MIT"} {"name":"mkdirp","version":"1.0.4","license":"MIT"} {"name":"yallist","version":"4.0.0","license":"ISC"} {"name":"text-table","version":"0.2.0","license":"MIT"} {"name":"tiny-relative-date","version":"1.3.0","license":"MIT"} {"name":"treeverse","version":"1.0.4","license":"ISC"} {"name":"validate-npm-package-name","version":"3.0.0","license":"ISC"} {"name":"builtins","version":"1.0.3","license":"MIT"} {"name":"which","version":"2.0.2","license":"ISC"} {"name":"isexe","version":"2.0.0","license":"ISC"} {"name":"write-file-atomic","version":"3.0.3","license":"ISC"} {"name":"imurmurhash","version":"0.1.4","license":"MIT"} {"name":"is-typedarray","version":"1.0.0","license":"MIT"} {"name":"signal-exit","version":"3.0.3","license":"ISC"} {"name":"typedarray-to-buffer","version":"3.1.5","license":"MIT"} {"name":"is-typedarray","version":"1.0.0","license":"MIT"} ```

With a significantly smaller runtime:

real    0m0.477s
user    0m0.590s
sys     0m0.068s
rnjudge commented 3 years ago

Thanks for this suggestion @JamieMagee -- those runtime improvements are awesome! Do you know the general availability of jq in most standard node/npm base images? That would be the only issue I could see using jq to parse instead of npm. In the mean time, let me get to work on a potential PR to incorporate and test this. Thanks!

rnjudge commented 3 years ago

Unfortunately, it doesn't look like jq is available as a default utility in the node base image and if it's not guaranteed to be available, we can't use it for our parsing. When I mount the base image using Tern's debug functionality:

(ternenv) root:# cat /etc/os-release 
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.11.6
PRETTY_NAME="Alpine Linux v3.11"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
(ternenv) root:# jq
/bin/sh: jq: not found

I agree that a run time of 25-30 minutes for npm package parsing is not helpful. We'll have to think about how to address this better. If you have any ideas, let me know.

JamieMagee commented 3 years ago

Yep, I realised soon after I opened this issue, that jq isn't a usual component of node based images.

However, I have an alternative that works on node:alpine.

Here's my JavaScript based method ```js "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const child_process_1 = require("child_process"); function getProjectUrl(project) { if (typeof project === 'string') { return project; } if ((project === null || project === void 0 ? void 0 : project.url) !== undefined) { return project.url; } return 'UNKNOWN'; } function parseDependencies(dependency) { var _a; let output = []; output.push({ name: dependency.name, version: dependency.version, license: (_a = dependency.license) !== null && _a !== void 0 ? _a : 'UNKNOWN', source: getProjectUrl(dependency.repository), }); if (dependency.dependencies) { Object.entries(dependency.dependencies).forEach(([key, value]) => { output.push(...parseDependencies(value)); }); } return output; } child_process_1.exec('npm list --global --depth=1 --long --json', (error, stdout, stderr) => { const npmList = JSON.parse(stdout); console.log(JSON.stringify([ ...Object.keys(npmList.dependencies) .map((key) => { return parseDependencies(npmList.dependencies[key]); }) .reduce((a, b) => a.concat(b), []), ])); }); //# sourceMappingURL=index.js.map ```
And the output of it when run on `node:alpine` ```json [ { "name": "npm", "version": "7.6.0", "license": "Artistic-2.0", "source": "https://github.com/npm/cli" }, { "name": "@npmcli/arborist", "version": "2.2.5", "license": "ISC", "source": "https://github.com/npm/arborist" }, { "name": "@npmcli/ci-detect", "version": "1.3.0", "license": "ISC", "source": "git+https://github.com/npm/ci-detect.git" }, { "name": "@npmcli/config", "version": "1.2.9", "license": "ISC", "source": "git+https://github.com/npm/config" }, { "name": "@npmcli/run-script", "version": "1.8.3", "license": "ISC", "source": "git+https://github.com/npm/run-script.git" }, { "name": "abbrev", "version": "1.1.1", "license": "ISC", "source": "http://github.com/isaacs/abbrev-js" }, { "name": "ansicolors", "version": "0.3.2", "license": "MIT", "source": "git://github.com/thlorenz/ansicolors.git" }, { "name": "ansistyles", "version": "0.1.3", "license": "MIT", "source": "git://github.com/thlorenz/ansistyles.git" }, { "name": "archy", "version": "1.0.0", "license": "MIT", "source": "http://github.com/substack/node-archy.git" }, { "name": "byte-size", "version": "7.0.0", "license": "MIT", "source": "https://github.com/75lb/byte-size" }, { "name": "cacache", "version": "15.0.5", "license": "ISC", "source": "https://github.com/npm/cacache" }, { "name": "chalk", "version": "4.1.0", "license": "MIT", "source": "chalk/chalk" }, { "name": "chownr", "version": "2.0.0", "license": "ISC", "source": "git://github.com/isaacs/chownr.git" }, { "name": "cli-columns", "version": "3.1.2", "license": "MIT", "source": "shannonmoeller/cli-columns" }, { "name": "cli-table3", "version": "0.6.0", "license": "MIT", "source": "https://github.com/cli-table/cli-table3.git" }, { "name": "columnify", "version": "1.5.4", "license": "MIT", "source": "git://github.com/timoxley/columnify.git" }, { "name": "glob", "version": "7.1.6", "license": "ISC", "source": "git://github.com/isaacs/node-glob.git" }, { "name": "graceful-fs", "version": "4.2.6", "license": "ISC", "source": "https://github.com/isaacs/node-graceful-fs" }, { "name": "hosted-git-info", "version": "3.0.8", "license": "ISC", "source": "git+https://github.com/npm/hosted-git-info.git" }, { "name": "ini", "version": "2.0.0", "license": "ISC", "source": "git://github.com/isaacs/ini.git" }, { "name": "init-package-json", "version": "2.0.2", "license": "ISC", "source": "https://github.com/npm/init-package-json.git" }, { "name": "is-cidr", "version": "4.0.2", "license": "BSD-2-Clause", "source": "silverwind/is-cidr" }, { "name": "json-parse-even-better-errors", "version": "2.3.1", "license": "MIT", "source": "https://github.com/npm/json-parse-even-better-errors" }, { "name": "leven", "version": "3.1.0", "license": "MIT", "source": "sindresorhus/leven" }, { "name": "libnpmaccess", "version": "4.0.1", "license": "ISC", "source": "https://github.com/npm/libnpmaccess.git" }, { "name": "libnpmdiff", "version": "2.0.3", "license": "ISC", "source": "https://github.com/npm/libnpmdiff" }, { "name": "libnpmfund", "version": "1.0.2", "license": "ISC", "source": "https://github.com/npm/libnpmfund" }, { "name": "libnpmhook", "version": "6.0.1", "license": "ISC", "source": "https://github.com/npm/libnpmhook" }, { "name": "libnpmorg", "version": "2.0.1", "license": "ISC", "source": "https://github.com/npm/libnpmorg.git" }, { "name": "libnpmpack", "version": "2.0.1", "license": "ISC", "source": "https://github.com/npm/libnpmpack.git" }, { "name": "libnpmpublish", "version": "4.0.0", "license": "ISC", "source": "https://github.com/npm/libnpmpublish.git" }, { "name": "libnpmsearch", "version": "3.1.0", "license": "ISC", "source": "https://github.com/npm/libnpmsearch.git" }, { "name": "libnpmteam", "version": "2.0.2", "license": "ISC", "source": "https://github.com/npm/libnpmteam.git" }, { "name": "libnpmversion", "version": "1.0.11", "license": "ISC", "source": "git+https://github.com/npm/libnpmversion" }, { "name": "make-fetch-happen", "version": "8.0.14", "license": "ISC", "source": "https://github.com/npm/make-fetch-happen" }, { "name": "minipass-pipeline", "version": "1.2.4", "license": "ISC", "source": "UNKNOWN" }, { "name": "minipass", "version": "3.1.3", "license": "ISC", "source": "git+https://github.com/isaacs/minipass.git" }, { "name": "mkdirp-infer-owner", "version": "2.0.0", "license": "ISC", "source": "git+https://github.com/isaacs/mkdirp-infer-owner" }, { "name": "mkdirp", "version": "1.0.4", "license": "MIT", "source": "https://github.com/isaacs/node-mkdirp.git" }, { "name": "ms", "version": "2.1.3", "license": "MIT", "source": "vercel/ms" }, { "name": "node-gyp", "version": "7.1.2", "license": "MIT", "source": "git://github.com/nodejs/node-gyp.git" }, { "name": "nopt", "version": "5.0.0", "license": "ISC", "source": "https://github.com/npm/nopt.git" }, { "name": "npm-audit-report", "version": "2.1.4", "license": "ISC", "source": "git+https://github.com/npm/npm-audit-report.git" }, { "name": "npm-package-arg", "version": "8.1.1", "license": "ISC", "source": "https://github.com/npm/npm-package-arg" }, { "name": "npm-pick-manifest", "version": "6.1.0", "license": "ISC", "source": "https://github.com/npm/npm-pick-manifest" }, { "name": "npm-profile", "version": "5.0.2", "license": "ISC", "source": "git+https://github.com/npm/npm-profile.git" }, { "name": "npm-registry-fetch", "version": "9.0.0", "license": "ISC", "source": "https://github.com/npm/npm-registry-fetch" }, { "name": "npm-user-validate", "version": "1.0.1", "license": "BSD-2-Clause", "source": "git://github.com/npm/npm-user-validate.git" }, { "name": "npmlog", "version": "4.1.2", "license": "ISC", "source": "https://github.com/npm/npmlog.git" }, { "name": "opener", "version": "1.5.2", "license": "(WTFPL OR MIT)", "source": "domenic/opener" }, { "name": "pacote", "version": "11.2.7", "license": "ISC", "source": "git@github.com:npm/pacote" }, { "name": "parse-conflict-json", "version": "1.1.1", "license": "ISC", "source": "git+https://github.com/npm/parse-conflict-json.git" }, { "name": "qrcode-terminal", "version": "0.12.0", "license": "UNKNOWN", "source": "https://github.com/gtanner/qrcode-terminal" }, { "name": "read-package-json-fast", "version": "2.0.2", "license": "ISC", "source": "git+https://github.com/npm/read-package-json-fast.git" }, { "name": "read-package-json", "version": "3.0.1", "license": "ISC", "source": "https://github.com/npm/read-package-json.git" }, { "name": "read", "version": "1.0.7", "license": "ISC", "source": "git://github.com/isaacs/read.git" }, { "name": "readdir-scoped-modules", "version": "1.1.0", "license": "ISC", "source": "https://github.com/npm/readdir-scoped-modules" }, { "name": "rimraf", "version": "3.0.2", "license": "ISC", "source": "git://github.com/isaacs/rimraf.git" }, { "name": "semver", "version": "7.3.4", "license": "ISC", "source": "https://github.com/npm/node-semver" }, { "name": "ssri", "version": "8.0.1", "license": "ISC", "source": "https://github.com/npm/ssri" }, { "name": "tar", "version": "6.1.0", "license": "ISC", "source": "https://github.com/npm/node-tar.git" }, { "name": "text-table", "version": "0.2.0", "license": "MIT", "source": "git://github.com/substack/text-table.git" }, { "name": "tiny-relative-date", "version": "1.3.0", "license": "MIT", "source": "https://github.com/wildlyinaccurate/relative-date.git" }, { "name": "treeverse", "version": "1.0.4", "license": "ISC", "source": "git+https://github.com/npm/treeverse.git" }, { "name": "validate-npm-package-name", "version": "3.0.0", "license": "ISC", "source": "https://github.com/npm/validate-npm-package-name" }, { "name": "which", "version": "2.0.2", "license": "ISC", "source": "git://github.com/isaacs/node-which.git" }, { "name": "write-file-atomic", "version": "3.0.3", "license": "ISC", "source": "git://github.com/npm/write-file-atomic.git" } ] ```

And best of all, it's still fast 🎉

real    0m 0.77s
user    0m 0.70s
sys     0m 0.07s

I also attempted importing npm, and calling it that way, but with the latest major release of npm (v7) they changed they signature of the list method. Given we can't know ahead of time the version of npm, it's easier to use child_process, and call npm that way.

Here's a gist with the TypeScript code.

rnjudge commented 3 years ago

@JamieMagee Instead of javascript, another thought would be to use the host system to parse npm packages (similar to your proposal for distroless) and add jq as a requirement of Tern. jq has no runtime dependencies so we wouldn't be introducing a long dependency chain.

JamieMagee commented 3 years ago

Sure, that also works for me. In that case we'll have to wait for #889 to land, as it has support for running commands on the host machine.

rnjudge commented 3 years ago

@JamieMagee Now that #889 has been merged, do you have time to take this up or do you want me to take a look?

JamieMagee commented 3 years ago

I was planning to take a look tomorrow. I'll reach out if I hit any blockers or need some pointers.