@exodus/stasis 1.0.0-beta.6 → 1.0.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/stasis.js +3 -2
- package/package.json +3 -3
- package/src/audit.js +21 -4
package/README.md
CHANGED
|
@@ -83,7 +83,7 @@ _Lockfiles (npm/pnpm/etc) not mentioned: they are like the "tarball" column, but
|
|
|
83
83
|
| `stasis audit --why-deep app.stasis.code.br` | like `--why`, but keep those longer chains too (every path, shared tails collapsed to `a -> b -> ... -> d`) |
|
|
84
84
|
| `stasis audit --why-full app.stasis.code.br` | like `--why`, but spell every chain out with no `...` collapse (combine with `--why-deep` for the complete raw listing) |
|
|
85
85
|
| `stasis audit --reason=run app.stasis.code.br` | show only advisories related to one consumer (`run`); with `--why`, keep only that consumer's chains |
|
|
86
|
-
| `GITHUB_TOKEN=... stasis audit --repo-advisories stasis.lock.json` | also ask each dependency's GitHub repository for the advisories its maintainers published there, which npm's database has only once GitHub reviews them; needs a GitHub token in `GITHUB_TOKEN`. Each package's repository is the one its artifact records (its `package.json`'s, see `repo` in [file formats](https://github.com/ExodusOSS/stasis/blob/main/doc/file-formats.md)), else it is looked up in npm's registry, Packagist or crates.io and cached for a month in the user cache directory (`$XDG_CACHE_HOME/stasis`, `~/.cache/stasis`, `~/Library/Caches/stasis` on macOS, `%LOCALAPPDATA%\stasis\Cache` on Windows) |
|
|
86
|
+
| `GITHUB_TOKEN=... stasis audit --repo-advisories stasis.lock.json` | also ask each dependency's GitHub repository for the advisories its maintainers published there, which npm's database has only once GitHub reviews them; needs a GitHub token in `GITHUB_TOKEN`. Each package's repository is the one its artifact records (its `package.json`'s, see `repo` in [file formats](https://github.com/ExodusOSS/stasis/blob/main/doc/file-formats.md)), else, for an npm package a bundle records none for but carries the `package.json` of (one built before the field), the one that names, else it is looked up in npm's registry, Packagist or crates.io and cached for a month in the user cache directory (`$XDG_CACHE_HOME/stasis`, `~/.cache/stasis`, `~/Library/Caches/stasis` on macOS, `%LOCALAPPDATA%\stasis\Cache` on Windows) |
|
|
87
87
|
| `stasis sbom --format=spdx stasis.lock.json` | export an SPDX SBOM for a lockfile or bundle |
|
|
88
88
|
| `stasis sbom --format=cyclonedx app.stasis.code.br` | export a CycloneDX SBOM for a lockfile or bundle |
|
|
89
89
|
|
package/bin/stasis.js
CHANGED
|
@@ -115,8 +115,9 @@ function usage(prefix = '') {
|
|
|
115
115
|
--reason=consumer shows only advisories related to that consumer, and with --why only its chains;
|
|
116
116
|
--repo-advisories also asks each package's GitHub repository for the advisories its maintainers
|
|
117
117
|
published there, before GitHub reviews them into the databases above; it needs a token in
|
|
118
|
-
GITHUB_TOKEN, and asks the repository each package's artifact records, else
|
|
119
|
-
|
|
118
|
+
GITHUB_TOKEN, and asks the repository each package's artifact records, else the one the
|
|
119
|
+
package.json a bundle carries of an npm package names, else looks one up and caches it for a
|
|
120
|
+
month in the user cache dir, e.g. ~/.cache/stasis)
|
|
120
121
|
stasis sbom --format=(spdx|cyclonedx) [--output=(path|-)] path/to/(lockfile|bundle) ...
|
|
121
122
|
(streams to stdout by default; --output=- is explicit stdout)
|
|
122
123
|
`.trim())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/stasis",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.7",
|
|
4
4
|
"description": "File-level source bundles and lockfiles",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
"homepage": "https://github.com/PreventiveMeasures/stasis#readme",
|
|
94
94
|
"dependencies": {
|
|
95
95
|
"@exodus/bytes": "^1.16.0",
|
|
96
|
-
"@exodus/stasis-core": "1.0.0-beta.
|
|
97
|
-
"@exodus/stasis-plugins": "1.0.0-beta.
|
|
96
|
+
"@exodus/stasis-core": "1.0.0-beta.7",
|
|
97
|
+
"@exodus/stasis-plugins": "1.0.0-beta.7",
|
|
98
98
|
"@preventive/archive": "1.0.0-beta.4",
|
|
99
99
|
"@preventive/deptree": "1.0.0-alpha.8",
|
|
100
100
|
"@preventive/lockfile": "1.0.0-alpha.7",
|
package/src/audit.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { packageRepo } from '@exodus/stasis-core/bundle-util'
|
|
1
2
|
import { dependencyEcosystem, moduleFileKey, sameGithub } from '@exodus/stasis-core/util'
|
|
2
3
|
import { advisories } from '@preventive/upstream/advisories.js'
|
|
3
4
|
import { compareVersions, valid } from '@preventive/upstream/semver.js'
|
|
4
5
|
import { isEvidenceFile } from './audit-corrections.js'
|
|
5
|
-
import { parseFile } from './parse.js'
|
|
6
|
+
import { parseFile, parseFileWithKind } from './parse.js'
|
|
6
7
|
import { collectWhy, invertReason } from './why.js'
|
|
7
8
|
|
|
8
9
|
// Where advisories() asks for each ecosystem's advisories: npm's registry; OSV for crates (those
|
|
@@ -57,21 +58,37 @@ function unaudited({ ecosystem, version }) {
|
|
|
57
58
|
// why.js apply the same evidence rule per file/edge there.
|
|
58
59
|
//
|
|
59
60
|
// A package's `github` is the repository its artifact records for it (its package.json's), where
|
|
60
|
-
// one does; a
|
|
61
|
+
// one does; else, for an npm package a bundle carries the package.json of (one built before builds
|
|
62
|
+
// recorded `repo`), the one that names, read by the rule builds record it by. A GitHub repo is its
|
|
63
|
+
// own, and has none.
|
|
61
64
|
export function collectPackagesFromFile(file) {
|
|
62
65
|
const out = []
|
|
63
|
-
|
|
66
|
+
const { kind, artifact } = parseFileWithKind(file)
|
|
67
|
+
for (const [dir, { name, version, ecosystem: tagged, repo, files }] of artifact.modules) {
|
|
64
68
|
const ecosystem = dependencyEcosystem(dir, tagged)
|
|
65
69
|
// First-party code is never sent to a public registry (leaks names, adds noise).
|
|
66
70
|
if (ecosystem === undefined) continue
|
|
67
71
|
if (!name || !version) continue
|
|
68
72
|
if (!Object.keys(files).some((rel) => isEvidenceFile(name, version, rel, ecosystem))) continue
|
|
69
|
-
|
|
73
|
+
let github = ecosystem === 'github' ? undefined : repo?.github
|
|
74
|
+
if (github === undefined && ecosystem === 'npm' && kind === 'bundle') github = manifestRepo(files['package.json'])
|
|
70
75
|
out.push({ ecosystem, name, version: versionOf(ecosystem, version), ...(github === undefined ? {} : { github }) })
|
|
71
76
|
}
|
|
72
77
|
return out
|
|
73
78
|
}
|
|
74
79
|
|
|
80
|
+
// The GitHub repository a bundled package.json's text names (packageRepo), or undefined where it
|
|
81
|
+
// names none or doesn't parse. Read as Node reads one, past a byte order mark (packageJSONText),
|
|
82
|
+
// which a bundle keeps in the text. A lockfile holds the file's digest alone, never asked here.
|
|
83
|
+
function manifestRepo(text) {
|
|
84
|
+
if (typeof text !== 'string') return undefined
|
|
85
|
+
try {
|
|
86
|
+
return packageRepo(JSON.parse(text.replace(/^\uFEFF/u, '')))?.github
|
|
87
|
+
} catch {
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
75
92
|
// Versions in semver's order where both are semver's, else as numbers in text compare.
|
|
76
93
|
const byNumbers = new Intl.Collator('en', { numeric: true }).compare
|
|
77
94
|
const byVersion = (a, b) => (valid(a) && valid(b) ? compareVersions(a, b) : byNumbers(a, b))
|