@pho9ubenaa/siro 0.5.1 → 0.6.1
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/CHANGELOG.md +81 -0
- package/README.md +60 -91
- package/dist/cli.mjs +64 -24
- package/dist/index.d.mts +106 -54
- package/dist/index.mjs +3 -3
- package/dist/{node-io-BDHg4J2w.mjs → node-io-BpAHyeBG.mjs} +1131 -829
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.1] — 2026-10-02
|
|
4
|
+
|
|
5
|
+
- Add `--no-config` to skip executable repository configuration, and
|
|
6
|
+
`--strict-filesystem` / API `rejectSymlinks` to reject native input-path symlinks.
|
|
7
|
+
Defaults retain automatic config and symlink resolution; neither option is a sandbox.
|
|
8
|
+
- Add finite scan/output budgets, caller overrides (`limits`, CLI `--max-*`) and
|
|
9
|
+
`DEFAULT_SCAN_LIMITS` / `ScanLimits` exports. Oversized/deep inputs now fail with
|
|
10
|
+
exit 2; output overflow exits 70 rather than producing a truncated success report.
|
|
11
|
+
- Omit source excerpts from JSON/YAML/TOML syntax diagnostics. Observed finding
|
|
12
|
+
values are not secret-redacted.
|
|
13
|
+
- Fix argument-limit failures when inspecting large Deno scoped import mappings.
|
|
14
|
+
- Update locked transitive dependencies to brace-expansion 5.0.12, markdown-it 14.3.1
|
|
15
|
+
and fast-uri 3.1.8 for upstream security fixes.
|
|
16
|
+
- Correct first-run safety examples to use `npx` without a global install.
|
|
17
|
+
- Separate build/verification from OIDC staging of the verified artifact, with
|
|
18
|
+
release identity, digest and main-ancestry checks.
|
|
19
|
+
|
|
20
|
+
Node requirements, direct dependency versions and decoded JSON schema 3 remain unchanged.
|
|
21
|
+
|
|
22
|
+
## [0.6.0] — 2026-09-29
|
|
23
|
+
|
|
24
|
+
### Breaking changes and inspection scope
|
|
25
|
+
|
|
26
|
+
- Discover package.json and strict deno.json recursively by default, independently
|
|
27
|
+
of PM workspace declarations. Remove `--workspaces` / API `workspaces`, including
|
|
28
|
+
false; legacy calls receive migration errors. PM exclusions no longer hide
|
|
29
|
+
fixtures, vendor or dist. Use `exclude: ['test/fixtures', 'vendor', 'dist']`, or
|
|
30
|
+
`exclude: ['**']` to keep only cwd.
|
|
31
|
+
- Add common exclusions and explicit `installationRoots` (default `['.']`). Arrays
|
|
32
|
+
replace config values; `[]` disables installation checks, not publication checks.
|
|
33
|
+
Example: `siro lint . --installation-root . --installation-root tools/standalone`.
|
|
34
|
+
Additional independent installation projects are not automatically inferred.
|
|
35
|
+
- Resolve PM/version locally: root options stay at cwd, additional roots use their
|
|
36
|
+
entry/local detection, and other packages use manifest-local evidence. Unknown
|
|
37
|
+
PMs still receive generic publication checks; unknown availability is not safety.
|
|
38
|
+
- JSON schema 3 adds `inspection`, required finding `directory`, and optional `pm`.
|
|
39
|
+
Generic publication findings run once per manifest without a synthetic PM.
|
|
40
|
+
- Require injected `FileSystem.readDirectories`; remove `resolveDirectory`. Skip
|
|
41
|
+
directory symlinks, `.git`, node_modules and explicit exclusions before reads.
|
|
42
|
+
No implicit vendor/dist/fixture exclusions or native filesystem fallback.
|
|
43
|
+
- `requireConfigKey.defaultSafety` explicitly controls safe-default downgrades;
|
|
44
|
+
omitted safety is conservative. VersionNote has no policy effect.
|
|
45
|
+
- Reporter calls require scan cwd as a third argument:
|
|
46
|
+
`await reporter.format(result, io, { cwd })`. Await built-in reporters and direct
|
|
47
|
+
IO writes, which may now complete asynchronously; handle output rejections.
|
|
48
|
+
Two-argument custom reporter implementations can ignore the additional context.
|
|
49
|
+
|
|
50
|
+
### Fixes
|
|
51
|
+
|
|
52
|
+
- Validate consumed Deno publication metadata before applicability and check exact
|
|
53
|
+
registry pins in both inline imports and scopes. Reject unrepresentable npmrc age
|
|
54
|
+
cutoffs rather than treating any positive integer as protection.
|
|
55
|
+
- Respect known pre-12 npm targets when checking npm-shrinkwrap.json; explain removed
|
|
56
|
+
or unknown-version lockfiles instead of incorrectly reporting that no file exists.
|
|
57
|
+
- GitHub annotations reference the correct absolute file using the supplied scan cwd.
|
|
58
|
+
API/JSON paths and schema 3 retain their existing meaning.
|
|
59
|
+
- Escape untrusted display controls and workflow markers without changing API paths
|
|
60
|
+
or parsed JSON values. Observe actual stream writes and preserve exit 70 on output
|
|
61
|
+
failure. IO may complete asynchronously; synchronous return values remain ignored.
|
|
62
|
+
- Reject Promise/thenable config exports and check results without an unhandled
|
|
63
|
+
rejection overriding the configuration-error exit. Keep lint synchronous.
|
|
64
|
+
- Deno empty/age-null/exclude-only objects no longer falsely satisfy release age.
|
|
65
|
+
Valid omitted ages use active local npmrc fallback, retaining zero opt-out and
|
|
66
|
+
explicit-age precedence. Leaf remedies preserve valid exclusions.
|
|
67
|
+
- npm own publishConfig.provenance overrides npmrc, including false. Validate its
|
|
68
|
+
consumed boolean type; align finding/remedy with the responsible file and keep
|
|
69
|
+
both locations' old-target availability guards.
|
|
70
|
+
- Preserve legal POSIX backslash and colon directory names without confusing them
|
|
71
|
+
with separators or portable user input. Reject malformed/traversing adapter names.
|
|
72
|
+
- Make every remediation operation path relative to scan cwd, including multi-file
|
|
73
|
+
remedies. Findings without a responsible file remain file-less.
|
|
74
|
+
- Fix Windows package verification for paths with spaces and shell metacharacters.
|
|
75
|
+
- Make one-shot and locally installed usage explicit, restore rule configuration
|
|
76
|
+
examples, and update moved documentation anchors in findings and the rule reference.
|
|
77
|
+
|
|
78
|
+
### Inspection limits
|
|
79
|
+
|
|
80
|
+
Installation checks use local settings, not inherited effective policy. Manifest-only
|
|
81
|
+
children have no provenance-policy checks, and child executable configs are not loaded.
|
|
82
|
+
See [scope and migration](docs/configuration.md) and [schema 3](docs/json-output.md).
|
|
83
|
+
|
|
3
84
|
## [0.5.1]
|
|
4
85
|
|
|
5
86
|
### Refactoring
|
package/README.md
CHANGED
|
@@ -4,115 +4,84 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@pho9ubenaa/siro)
|
|
5
5
|
[](https://github.com/pHo9UBenaA/siro/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A security-configuration linter for npm, pnpm, Yarn, Bun, Deno, and Aube.
|
|
8
|
+
It reports supported dependency-installation and publication policy gaps, such as permissive
|
|
9
|
+
lifecycle scripts, unpinned versions, and missing publication safeguards. It does not install
|
|
10
|
+
packages or change your files.
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
| Approach | Primary question | Typical input |
|
|
13
|
+
| ----------------------------- | --------------------------------------------- | -------------------------------------------- |
|
|
14
|
+
| siro: configuration lint | Are supported install/publish settings risky? | Repository manifests and configuration files |
|
|
15
|
+
| Dependency vulnerability scan | Do dependencies match known advisories? | Dependency inventory and vulnerability data |
|
|
16
|
+
| Dependency update automation | Which dependencies can be updated? | Manifests, lockfiles, and package registries |
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
These approaches complement one another; a clean result does not guarantee safety.
|
|
19
|
+
|
|
20
|
+
## Try it
|
|
21
|
+
|
|
22
|
+
Requires Node.js `^22.18.0` or `^24.0.0`. From your repository:
|
|
19
23
|
|
|
20
24
|
```sh
|
|
21
|
-
npx @pho9ubenaa/siro lint
|
|
22
|
-
npx @pho9ubenaa/siro lint --reporter json # machine-readable remediation (see docs/json-output.md)
|
|
25
|
+
npx @pho9ubenaa/siro lint
|
|
23
26
|
```
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
siro recursively discovers package.json and strict deno.json below cwd, independently of PM
|
|
29
|
+
workspace declarations. It checks local installation policy at cwd by default; add independent
|
|
30
|
+
projects with `--installation-root`. Exclude intentional fixtures with `--exclude test/fixtures`.
|
|
31
|
+
Discovery does not imply that every package's installation settings were inspected.
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
siro detects managers from `packageManager`, lockfiles, and configuration files. If it cannot
|
|
34
|
+
detect one, choose it explicitly, for example `npx @pho9ubenaa/siro lint --pm npm`.
|
|
35
|
+
The CLI may download code through `npx` and imports a repository's `siro.config.*` as executable
|
|
36
|
+
code by default. For an unfamiliar project, use `npx @pho9ubenaa/siro lint --no-config --strict-filesystem`
|
|
37
|
+
in an isolated environment without credentials. These flags do not sandbox npx or
|
|
38
|
+
extensions; see the [threat model](docs/threat-model.md).
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
# .npmrc
|
|
31
|
-
-ignore-scripts=false
|
|
32
|
-
+ignore-scripts=true
|
|
33
|
-
```
|
|
40
|
+
## Read findings and add CI
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
## Features
|
|
40
|
-
|
|
41
|
-
- **Rules across six managers.** 28 rules covering lifecycle scripts, version pinning, lockfiles
|
|
42
|
-
(`commit`/`frozen`), release age, publish provenance, `files`/`publishConfig`, SSL enforcement,
|
|
43
|
-
checksum verification, exotic subdependency blocking, audit suppression review, store integrity,
|
|
44
|
-
Bun's security scanner API, and Yarn 4's hardened-mode — each mapped to the right setting per
|
|
45
|
-
package manager (`.npmrc`, `pnpm-workspace.yaml`, `.yarnrc.yml`, `bunfig.toml`, `deno.json`,
|
|
46
|
-
`aube-workspace.yaml`, `package.json`).
|
|
47
|
-
- **PM-aware severities.** When a manager's documented default satisfies a rule across every
|
|
48
|
-
supported version and target environment, the finding is demoted to `info`. Installed versions
|
|
49
|
-
and CI conditions are not checked, so defaults that depend on either retain the rule's severity.
|
|
50
|
-
- **Machine-readable remediation.** A finding can carry automatic key operations or manual
|
|
51
|
-
instructions. Review proposed changes and rerun the linter after editing.
|
|
52
|
-
See [docs/json-output.md](docs/json-output.md).
|
|
53
|
-
- **Target PM versions.** Flag settings introduced after the declared or explicit stable PM
|
|
54
|
-
version. See [checked settings and sources](docs/rules.md#checked-introduction-versions)
|
|
55
|
-
for the npm, pnpm, Yarn, Bun, and Deno coverage.
|
|
56
|
-
- **Workspace members.** `--workspaces` adds publication-metadata checks for declared npm,
|
|
57
|
-
pnpm, Yarn, Bun, Deno, and Aube members, including public packages under a private root.
|
|
58
|
-
See [workspace inspection](docs/configuration.md#workspace-members) for scope and exclusions.
|
|
59
|
-
- **Lint with severities.** `error` fails CI by default; `--severity warn` tightens the gate.
|
|
60
|
-
- **Reporters.** `pretty` (default), `json` for CI, `github` for PR annotations; register your own.
|
|
61
|
-
- **Configurable.** Drop a `siro.config.ts` to disable rules, override severities, restrict PMs,
|
|
62
|
-
or plug in custom rules and reporters.
|
|
63
|
-
|
|
64
|
-
See the [rule reference](docs/rules.md) for what each check does and why, and the
|
|
65
|
-
[comparison matrix](docs/comparison.md) for per-manager support at a glance.
|
|
66
|
-
|
|
67
|
-
## Versioning policy
|
|
68
|
-
|
|
69
|
-
siro evaluates the recorded policy snapshot in [docs/policy-sources.md](docs/policy-sources.md).
|
|
70
|
-
It detects package-manager names and reads exact stable targets from `packageManager`,
|
|
71
|
-
`config.pmVersions`, or `--pm-version`. It does not inspect installed binaries. The
|
|
72
|
-
`unsupported-settings` rule checks recorded introduction versions; unlisted settings and
|
|
73
|
-
unknown targets are not evaluated for availability. A version-dependent safe-default annotation prevents an unverified
|
|
74
|
-
severity downgrade; it does not prove that the current version satisfies the rule. See
|
|
75
|
-
[configuration](docs/configuration.md) for defaults, applicability, and limits.
|
|
76
|
-
|
|
77
|
-
## Usage
|
|
42
|
+
Findings have `error`, `warn`, or `info` severity. Exit `0` means no findings at or above the
|
|
43
|
+
selected threshold; exit `1` means there are findings. Usage/configuration errors exit `2`
|
|
44
|
+
without completing the check; unexpected failures exit `70`. Errors fail CI by default. siro
|
|
45
|
+
suggests fixes but **does not edit files**: review changes and rerun the linter.
|
|
78
46
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
--pm <npm|pnpm|yarn|bun|deno|aube> Target a specific package manager (auto-detected; required if detection finds nothing)
|
|
83
|
-
--pm-version <x.y.z> Exact stable target version (requires --pm)
|
|
84
|
-
--workspaces Also inspect workspace members' publication metadata
|
|
85
|
-
--project-type <application|package> Select application or published-package policy (default auto)
|
|
86
|
-
--reporter <pretty|json|github> Output format (default pretty)
|
|
87
|
-
--severity <error|warn|info> Show and fail on findings at or above this level
|
|
88
|
-
--json Shortcut for --reporter json
|
|
89
|
-
--version, --help
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
`check` is an alias of `lint` (same flags, same exit codes) — provided so `siro check` reads naturally in CI scripts.
|
|
47
|
+
For regular use, install it with `npm install --save-dev --save-exact @pho9ubenaa/siro`
|
|
48
|
+
and add a package script:
|
|
93
49
|
|
|
94
|
-
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"scripts": { "lint:security": "siro lint" }
|
|
53
|
+
}
|
|
54
|
+
```
|
|
95
55
|
|
|
96
|
-
|
|
56
|
+
After installing dependencies in CI, run `npm run lint:security`. A local install
|
|
57
|
+
makes `siro` available to package scripts, not to every shell or Git hook.
|
|
58
|
+
For exclusions and rule overrides, see the [configuration examples](docs/configuration.md).
|
|
97
59
|
|
|
98
|
-
|
|
60
|
+
## Common CLI options
|
|
99
61
|
|
|
100
|
-
|
|
101
|
-
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
102
|
-
| `npm audit` · [osv-scanner](https://github.com/google/osv-scanner) · Snyk · Dependabot | **Known CVEs** in your installed dependency tree | GHSA / OSV.dev / vendor feeds |
|
|
103
|
-
| `siro` | **Your install pipeline's configuration** — postinstall scripts, version ranges, lockfile policy, publish provenance, files allow-list, etc. | Static analysis of `.npmrc`, `pnpm-workspace.yaml`, `.yarnrc.yml`, `bunfig.toml`, `deno.json`, `aube-workspace.yaml`, `package.json` |
|
|
62
|
+
`check` is an alias for `lint`. Run `npx @pho9ubenaa/siro lint --help` for the complete CLI syntax.
|
|
104
63
|
|
|
105
|
-
|
|
64
|
+
| Option | Use |
|
|
65
|
+
| ----------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
66
|
+
| `--pm <npm\|pnpm\|yarn\|bun\|deno\|aube>` | Select one manager at cwd; additional installation roots retain their own targets. |
|
|
67
|
+
| `--pm-version <x.y.z>` | Supply an exact stable target version (requires `--pm`); it does not run an installed PM. |
|
|
68
|
+
| `--project-type <application\|package>` | Choose whether publication safeguards apply; omitted means infer from publish metadata. |
|
|
69
|
+
| `--exclude <pattern>` | Prune directories from recursive discovery (repeatable). |
|
|
70
|
+
| `--installation-root <path>` | Replace the default cwd installation scope (repeatable; include `.` to retain cwd). |
|
|
71
|
+
| `--severity <error\|warn\|info>` | Set both the display and CI failure threshold; default failure threshold is `error`. |
|
|
72
|
+
| `--reporter <pretty\|json\|github>` | Choose terminal, JSON, or GitHub Actions output; `--json` is a JSON shortcut. |
|
|
106
73
|
|
|
107
|
-
|
|
74
|
+
`--no-config` disables executable configuration; `--strict-filesystem` rejects
|
|
75
|
+
symlink input paths in native data reads. Scans also have finite caller-controlled
|
|
76
|
+
[file, tree, nesting, finding and output budgets](docs/configuration.md#strict-filesystem-and-scan-budgets).
|
|
77
|
+
Overflow fails the check rather than silently skipping inputs.
|
|
108
78
|
|
|
109
|
-
|
|
110
|
-
[docs/contributing.md](docs/contributing.md).
|
|
79
|
+
For a walkthrough and deeper reference, use these guides:
|
|
111
80
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
[
|
|
81
|
+
- [Getting started](docs/getting-started.md) walks through findings and CI; [configuration](docs/configuration.md) covers local PM/version selection, discovery and explicit installation scope, executable config, exit codes, and migration from the removed `--workspaces` flag.
|
|
82
|
+
- The [rule reference](docs/rules.md) and [PM comparison](docs/comparison.md) show what is checked for each manager.
|
|
83
|
+
- [JSON output](docs/json-output.md) documents the machine-readable remediation contract.
|
|
84
|
+
- [Contributing](docs/contributing.md) covers development setup, the source map, and verification.
|
|
116
85
|
|
|
117
86
|
## License
|
|
118
87
|
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as
|
|
2
|
+
import { C as isSeverity, D as asAbsPath, E as version, F as SiroError, I as UsageError, M as DEFAULT_SCAN_LIMITS, N as resolveScanLimits, S as isPM, T as SUPPORTED_NODE_RANGE, _ as PROJECT_TYPES, a as DEFAULT_REPORTER_NAME, b as PMS, i as BUILTIN_REPORTER_NAMES, j as isNodeError, k as assertDirectory, l as safeText, m as loadConfig, o as JSON_REPORTER_NAME, r as lintCommand, t as nodeIO, v as isProjectType, w as isSupportedNodeVersion, x as SEVERITIES, y as isStableVersion } from "./node-io-BpAHyeBG.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
@@ -31,18 +31,23 @@ const COMMANDS = ["lint", "check"];
|
|
|
31
31
|
const isCommandName = (value) => COMMANDS.some((cmd) => cmd === value);
|
|
32
32
|
//#endregion
|
|
33
33
|
//#region src/cli/parse-args.ts
|
|
34
|
+
const LIMIT_FLAGS = new Map(Object.keys(DEFAULT_SCAN_LIMITS).map((key) => [key.replace(/[A-Z]/gu, (letter) => `-${letter.toLowerCase()}`), key]));
|
|
35
|
+
const REPEATABLE_FLAGS = new Set(["exclude", "installation-root"]);
|
|
34
36
|
const VALUE_FLAGS = new Set([
|
|
35
37
|
"pm",
|
|
36
38
|
"pm-version",
|
|
37
39
|
"project-type",
|
|
38
40
|
"reporter",
|
|
39
|
-
"severity"
|
|
41
|
+
"severity",
|
|
42
|
+
...REPEATABLE_FLAGS,
|
|
43
|
+
...LIMIT_FLAGS.keys()
|
|
40
44
|
]);
|
|
41
45
|
const BOOLEAN_FLAGS = new Set([
|
|
42
46
|
"help",
|
|
43
47
|
"version",
|
|
44
48
|
"json",
|
|
45
|
-
"
|
|
49
|
+
"no-config",
|
|
50
|
+
"strict-filesystem"
|
|
46
51
|
]);
|
|
47
52
|
const parseCommand = (argv) => {
|
|
48
53
|
const { tokens } = parseArgs({
|
|
@@ -61,6 +66,7 @@ const parseCommand = (argv) => {
|
|
|
61
66
|
tokens: true
|
|
62
67
|
});
|
|
63
68
|
const flags = /* @__PURE__ */ new Map();
|
|
69
|
+
const repeated = /* @__PURE__ */ new Map();
|
|
64
70
|
const positionals = [];
|
|
65
71
|
let error;
|
|
66
72
|
for (let index = 0; index < tokens.length; index += 1) {
|
|
@@ -76,7 +82,11 @@ const parseCommand = (argv) => {
|
|
|
76
82
|
}
|
|
77
83
|
if (BOOLEAN_FLAGS.has(token.name)) if (token.value !== void 0) error ??= `Flag ${token.rawName} does not accept a value.`;
|
|
78
84
|
else {
|
|
79
|
-
if (
|
|
85
|
+
if ([
|
|
86
|
+
"json",
|
|
87
|
+
"no-config",
|
|
88
|
+
"strict-filesystem"
|
|
89
|
+
].includes(token.name) && flags.has(token.name)) error ??= `${token.rawName} must be specified only once.`;
|
|
80
90
|
flags.set(token.name, true);
|
|
81
91
|
}
|
|
82
92
|
else if (VALUE_FLAGS.has(token.name)) {
|
|
@@ -87,11 +97,12 @@ const parseCommand = (argv) => {
|
|
|
87
97
|
index += 1;
|
|
88
98
|
}
|
|
89
99
|
if (value === void 0 || value === "") error ??= `${token.rawName} requires a value.`;
|
|
100
|
+
else if (REPEATABLE_FLAGS.has(token.name)) repeated.set(token.name, [...repeated.get(token.name) ?? [], value]);
|
|
90
101
|
else {
|
|
91
102
|
if (flags.has(token.name)) error ??= `${token.rawName} must be specified only once.`;
|
|
92
103
|
flags.set(token.name, value);
|
|
93
104
|
}
|
|
94
|
-
} else error ??= `Unknown flag: ${token.rawName}`;
|
|
105
|
+
} else error ??= token.name === "workspaces" ? "The --workspaces flag was removed in 0.6.0; discovery is recursive by default. Use --exclude and --installation-root." : `Unknown flag: ${token.rawName}`;
|
|
95
106
|
}
|
|
96
107
|
const [command, cwd, ...extra] = positionals;
|
|
97
108
|
if (flags.has("help")) return {
|
|
@@ -113,14 +124,29 @@ const parseCommand = (argv) => {
|
|
|
113
124
|
if (flags.has("reporter") && flags.has("json")) throw new UsageError("Invalid reporter selection: use either --reporter or --json.");
|
|
114
125
|
const reporter = flags.get("reporter");
|
|
115
126
|
const pmVersion = flags.get("pm-version");
|
|
127
|
+
const pm = parsePmFlag(flags.get("pm"));
|
|
128
|
+
if (pmVersion !== void 0 && (!pm || !isStableVersion(pmVersion))) throw new UsageError("--pm-version requires --pm and an exact stable version.");
|
|
129
|
+
const limits = {};
|
|
130
|
+
for (const [flag, key] of LIMIT_FLAGS) {
|
|
131
|
+
const value = flags.get(flag);
|
|
132
|
+
if (value !== void 0) {
|
|
133
|
+
if (typeof value !== "string" || !/^\d+$/u.test(value)) throw new UsageError(`${flag} must be a positive safe integer.`);
|
|
134
|
+
limits[key] = Number(value);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
resolveScanLimits(limits);
|
|
116
138
|
return {
|
|
117
139
|
kind: "lint",
|
|
118
140
|
cwd: asAbsPath(path.resolve(cwd ?? process.cwd())),
|
|
119
|
-
pm
|
|
141
|
+
pm,
|
|
120
142
|
pmVersion: typeof pmVersion === "string" ? pmVersion : void 0,
|
|
121
|
-
|
|
143
|
+
exclude: repeated.get("exclude"),
|
|
144
|
+
installationRoots: repeated.get("installation-root"),
|
|
122
145
|
projectType: parseProjectTypeFlag(flags.get("project-type")),
|
|
123
146
|
severity: parseSeverityFlag(flags.get("severity")),
|
|
147
|
+
...flags.has("no-config") ? { noConfig: true } : {},
|
|
148
|
+
...flags.has("strict-filesystem") ? { rejectSymlinks: true } : {},
|
|
149
|
+
...Object.keys(limits).length ? { limits } : {},
|
|
124
150
|
reporter: typeof reporter === "string" ? reporter : flags.has("json") ? JSON_REPORTER_NAME : DEFAULT_REPORTER_NAME
|
|
125
151
|
};
|
|
126
152
|
};
|
|
@@ -132,9 +158,11 @@ const PROJECT_TYPES_LIST = PROJECT_TYPES.join("|");
|
|
|
132
158
|
const SEVERITIES_LIST = SEVERITIES.join("|");
|
|
133
159
|
const FLAG_LINES = {
|
|
134
160
|
json: " --json Shortcut for --reporter json",
|
|
161
|
+
safety: " --no-config Do not probe or execute repository siro.config.*\n --strict-filesystem Reject symlink input paths (not a containment sandbox)",
|
|
162
|
+
limits: " --max-file-bytes <n> Bound each input file (default 8388608)\n --max-total-bytes <n> Bound all input reads (default 67108864)\n --max-entries <n> Bound native directory entries (default 100000)\n --max-directories <n> Bound discovered directories (default 10000)\n --max-directory-depth <n> Bound discovery depth (default 128)\n --max-config-depth <n> Bound configuration nesting (default 128)\n --max-findings <n> Bound unfiltered findings (default 50000)\n --max-output-bytes <n> Bound report output (default 33554432)",
|
|
135
163
|
pm: ` --pm <name> Target a specific package manager (${PMS_LIST})`,
|
|
136
164
|
pmVersion: " --pm-version <x.y.z> Target an exact stable PM version (requires --pm)",
|
|
137
|
-
|
|
165
|
+
inspection: " --exclude <pattern> Exclude directories from recursive discovery (repeatable)\n --installation-root <path> Inspect local install policy here (repeatable; default .)",
|
|
138
166
|
projectType: ` --project-type <type> Project type (${PROJECT_TYPES_LIST}; default auto)`,
|
|
139
167
|
reporter: ` --reporter <name> Reporter (${REPORTERS_LIST}; additional reporters can be registered via siro.config.ts)`,
|
|
140
168
|
severity: ` --severity <level> Show + fail on findings at or above this level (${SEVERITIES_LIST})`
|
|
@@ -151,18 +179,21 @@ const HELP_ROOT = [
|
|
|
151
179
|
"GLOBAL FLAGS",
|
|
152
180
|
FLAG_LINES.pm,
|
|
153
181
|
FLAG_LINES.pmVersion,
|
|
154
|
-
FLAG_LINES.
|
|
182
|
+
FLAG_LINES.inspection,
|
|
155
183
|
FLAG_LINES.projectType,
|
|
156
184
|
" --version Print the siro version",
|
|
157
185
|
" --help Show help for siro or a command",
|
|
158
186
|
"",
|
|
159
187
|
"LINT FLAGS",
|
|
188
|
+
FLAG_LINES.safety,
|
|
189
|
+
FLAG_LINES.limits,
|
|
160
190
|
FLAG_LINES.reporter,
|
|
161
191
|
FLAG_LINES.json,
|
|
162
192
|
FLAG_LINES.severity,
|
|
163
193
|
"",
|
|
164
194
|
"EXAMPLES",
|
|
165
195
|
" $ siro lint # report violations in cwd",
|
|
196
|
+
" $ siro lint --no-config --strict-filesystem # data-only inspection",
|
|
166
197
|
" $ siro lint --reporter github # GitHub Actions annotations",
|
|
167
198
|
" $ siro lint --severity warn # also fail on warnings",
|
|
168
199
|
"",
|
|
@@ -179,9 +210,11 @@ const HELP_LINT = [
|
|
|
179
210
|
" siro check [path] [flags] (alias)",
|
|
180
211
|
"",
|
|
181
212
|
"FLAGS",
|
|
213
|
+
FLAG_LINES.safety,
|
|
214
|
+
FLAG_LINES.limits,
|
|
182
215
|
FLAG_LINES.pm,
|
|
183
216
|
FLAG_LINES.pmVersion,
|
|
184
|
-
FLAG_LINES.
|
|
217
|
+
FLAG_LINES.inspection,
|
|
185
218
|
FLAG_LINES.projectType,
|
|
186
219
|
FLAG_LINES.reporter,
|
|
187
220
|
FLAG_LINES.json,
|
|
@@ -191,10 +224,11 @@ const HELP_LINT = [
|
|
|
191
224
|
" 0 No findings at or above the active threshold",
|
|
192
225
|
" 1 Findings at or above the threshold (default: error)",
|
|
193
226
|
" 2 Usage error (bad flag, broken siro.config.ts, unreadable path, …)",
|
|
194
|
-
" 70
|
|
227
|
+
" 70 Output failure/limit, or unexpected exception (including trusted extensions)",
|
|
195
228
|
"",
|
|
196
229
|
"EXAMPLES",
|
|
197
230
|
" $ siro lint # default: pretty reporter, fail on errors",
|
|
231
|
+
" $ siro lint --no-config --strict-filesystem # data-only inspection",
|
|
198
232
|
" $ siro lint --reporter github # GitHub Actions annotations",
|
|
199
233
|
" $ siro lint --severity warn # tighten the gate"
|
|
200
234
|
].join("\n");
|
|
@@ -208,34 +242,38 @@ const renderHelp = (target) => {
|
|
|
208
242
|
const EXIT_SUCCESS = 0;
|
|
209
243
|
const EXIT_USAGE = 2;
|
|
210
244
|
const EXIT_CRASH = 70;
|
|
211
|
-
const dispatch = (cmd, io) => {
|
|
245
|
+
const dispatch = async (cmd, io) => {
|
|
212
246
|
switch (cmd.kind) {
|
|
213
247
|
case "version":
|
|
214
|
-
io.stdout(version);
|
|
248
|
+
await io.stdout(version);
|
|
215
249
|
return EXIT_SUCCESS;
|
|
216
250
|
case "help":
|
|
217
|
-
io.stdout(renderHelp(cmd.target));
|
|
251
|
+
await io.stdout(renderHelp(cmd.target));
|
|
218
252
|
return EXIT_SUCCESS;
|
|
219
253
|
case "usage":
|
|
220
|
-
if (cmd.reason) io.stderr(`${cmd.reason}\n`);
|
|
221
|
-
io.stderr(renderHelp());
|
|
254
|
+
if (cmd.reason) await io.stderr(`${safeText(cmd.reason)}\n`);
|
|
255
|
+
await io.stderr(renderHelp());
|
|
222
256
|
return EXIT_USAGE;
|
|
223
|
-
case "lint":
|
|
257
|
+
case "lint": {
|
|
224
258
|
assertDirectory(cmd.cwd);
|
|
225
|
-
|
|
226
|
-
|
|
259
|
+
const { noConfig, ...options } = cmd;
|
|
260
|
+
if (noConfig) return lintCommand(options, io);
|
|
261
|
+
const config = await loadConfig(cmd.cwd);
|
|
262
|
+
return lintCommand({
|
|
263
|
+
...options,
|
|
227
264
|
config
|
|
228
|
-
}, io)
|
|
265
|
+
}, io);
|
|
266
|
+
}
|
|
229
267
|
default: throw new Error(`Unhandled command kind: ${String(cmd)}`);
|
|
230
268
|
}
|
|
231
269
|
};
|
|
232
|
-
const handleError = (error, io) => {
|
|
270
|
+
const handleError = async (error, io) => {
|
|
233
271
|
if (error instanceof SiroError) {
|
|
234
|
-
io.stderr(error.message);
|
|
272
|
+
await io.stderr(safeText(error.message));
|
|
235
273
|
return error.exitCode;
|
|
236
274
|
}
|
|
237
275
|
if (isNodeError(error) && "errno" in error && typeof error.errno === "number") {
|
|
238
|
-
io.stderr(`File system error: ${error.message}`);
|
|
276
|
+
await io.stderr(safeText(`File system error: ${error.message}`));
|
|
239
277
|
return EXIT_USAGE;
|
|
240
278
|
}
|
|
241
279
|
throw error;
|
|
@@ -253,8 +291,10 @@ const runMain = async (argv) => {
|
|
|
253
291
|
process.exitCode = await run(argv);
|
|
254
292
|
} catch (error) {
|
|
255
293
|
const errStr = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
256
|
-
process.stderr.write(`${errStr}\n`);
|
|
257
294
|
process.exitCode = EXIT_CRASH;
|
|
295
|
+
try {
|
|
296
|
+
await nodeIO.stderr(safeText(errStr));
|
|
297
|
+
} catch {}
|
|
258
298
|
}
|
|
259
299
|
};
|
|
260
300
|
const [, invokedPath] = process.argv;
|