@pho9ubenaa/siro 0.6.0 → 0.6.2
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 +29 -0
- package/README.md +8 -2
- package/dist/cli.mjs +6 -269
- package/dist/index.d.mts +50 -34
- package/dist/index.mjs +1 -7
- package/dist/node-io-DE7Nzlal.mjs +7 -0
- package/package.json +1 -1
- package/dist/node-io-CAszdmko.mjs +0 -3063
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.2] — 2026-10-03
|
|
4
|
+
|
|
5
|
+
- Report oversized custom rule groups as finding-limit errors (CLI exit 2), rather
|
|
6
|
+
than JavaScript argument-limit failures (exit 70). Larger caller-supplied finding
|
|
7
|
+
budgets also work without truncating results.
|
|
8
|
+
- Reduce distribution size and repeated configuration work during scans.
|
|
9
|
+
|
|
10
|
+
Public API, CLI options, decoded JSON schema 3, Node requirements, inspection scope
|
|
11
|
+
and [safety limits](docs/threat-model.md) remain unchanged.
|
|
12
|
+
|
|
13
|
+
## [0.6.1] — 2026-10-02
|
|
14
|
+
|
|
15
|
+
- Add `--no-config` to skip executable repository configuration, and
|
|
16
|
+
`--strict-filesystem` / API `rejectSymlinks` to reject native input-path symlinks.
|
|
17
|
+
Defaults retain automatic config and symlink resolution; neither option is a sandbox.
|
|
18
|
+
- Add finite scan/output budgets, caller overrides (`limits`, CLI `--max-*`) and
|
|
19
|
+
`DEFAULT_SCAN_LIMITS` / `ScanLimits` exports. Oversized/deep inputs now fail with
|
|
20
|
+
exit 2; output overflow exits 70 rather than producing a truncated success report.
|
|
21
|
+
- Omit source excerpts from JSON/YAML/TOML syntax diagnostics. Observed finding
|
|
22
|
+
values are not secret-redacted.
|
|
23
|
+
- Fix argument-limit failures when inspecting large Deno scoped import mappings.
|
|
24
|
+
- Update locked transitive dependencies to brace-expansion 5.0.12, markdown-it 14.3.1
|
|
25
|
+
and fast-uri 3.1.8 for upstream security fixes.
|
|
26
|
+
- Correct first-run safety examples to use `npx` without a global install.
|
|
27
|
+
- Separate build/verification from OIDC staging of the verified artifact, with
|
|
28
|
+
release identity, digest and main-ancestry checks.
|
|
29
|
+
|
|
30
|
+
Node requirements, direct dependency versions and decoded JSON schema 3 remain unchanged.
|
|
31
|
+
|
|
3
32
|
## [0.6.0] — 2026-09-29
|
|
4
33
|
|
|
5
34
|
### Breaking changes and inspection scope
|
package/README.md
CHANGED
|
@@ -33,8 +33,9 @@ Discovery does not imply that every package's installation settings were inspect
|
|
|
33
33
|
siro detects managers from `packageManager`, lockfiles, and configuration files. If it cannot
|
|
34
34
|
detect one, choose it explicitly, for example `npx @pho9ubenaa/siro lint --pm npm`.
|
|
35
35
|
The CLI may download code through `npx` and imports a repository's `siro.config.*` as executable
|
|
36
|
-
code.
|
|
37
|
-
|
|
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).
|
|
38
39
|
|
|
39
40
|
## Read findings and add CI
|
|
40
41
|
|
|
@@ -70,6 +71,11 @@ For exclusions and rule overrides, see the [configuration examples](docs/configu
|
|
|
70
71
|
| `--severity <error\|warn\|info>` | Set both the display and CI failure threshold; default failure threshold is `error`. |
|
|
71
72
|
| `--reporter <pretty\|json\|github>` | Choose terminal, JSON, or GitHub Actions output; `--json` is a JSON shortcut. |
|
|
72
73
|
|
|
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.
|
|
78
|
+
|
|
73
79
|
For a walkthrough and deeper reference, use these guides:
|
|
74
80
|
|
|
75
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.
|
package/dist/cli.mjs
CHANGED
|
@@ -1,271 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
if (typeof raw === "undefined") return;
|
|
9
|
-
if (raw === true) throw new UsageError(`--pm requires a value (expected one of: ${PMS.join(", ")})`);
|
|
10
|
-
if (typeof raw !== "string" || !isPM(raw)) throw new UsageError(`Unknown package manager: ${raw} (expected one of: ${PMS.join(", ")})`);
|
|
11
|
-
return raw;
|
|
12
|
-
};
|
|
13
|
-
const parseProjectTypeFlag = (raw) => {
|
|
14
|
-
if (typeof raw === "undefined") return;
|
|
15
|
-
if (raw === true) throw new UsageError(`--project-type requires a value (expected ${PROJECT_TYPES.join("|")})`);
|
|
16
|
-
if (typeof raw === "string" && isProjectType(raw)) return raw;
|
|
17
|
-
throw new UsageError(`Unknown project type: ${String(raw)} (expected ${PROJECT_TYPES.join("|")})`);
|
|
18
|
-
};
|
|
19
|
-
const parseSeverityFlag = (raw) => {
|
|
20
|
-
if (typeof raw === "undefined") return;
|
|
21
|
-
if (raw === true) throw new UsageError(`--severity requires a value (expected ${SEVERITIES.join("|")})`);
|
|
22
|
-
if (typeof raw !== "string" || !isSeverity(raw)) throw new UsageError(`Invalid severity: ${String(raw)} (expected ${SEVERITIES.join("|")})`);
|
|
23
|
-
return raw;
|
|
24
|
-
};
|
|
25
|
-
const ensureNodeVersion = (nodeVersion) => {
|
|
26
|
-
if (!isSupportedNodeVersion(nodeVersion)) throw new UsageError(`Node ${SUPPORTED_NODE_RANGE} required (you are on ${nodeVersion}). Upgrade your runtime to use siro.`);
|
|
27
|
-
};
|
|
28
|
-
//#endregion
|
|
29
|
-
//#region src/cli/commands.ts
|
|
30
|
-
const COMMANDS = ["lint", "check"];
|
|
31
|
-
const isCommandName = (value) => COMMANDS.some((cmd) => cmd === value);
|
|
32
|
-
//#endregion
|
|
33
|
-
//#region src/cli/parse-args.ts
|
|
34
|
-
const REPEATABLE_FLAGS = new Set(["exclude", "installation-root"]);
|
|
35
|
-
const VALUE_FLAGS = new Set([
|
|
36
|
-
"pm",
|
|
37
|
-
"pm-version",
|
|
38
|
-
"project-type",
|
|
39
|
-
"reporter",
|
|
40
|
-
"severity",
|
|
41
|
-
...REPEATABLE_FLAGS
|
|
42
|
-
]);
|
|
43
|
-
const BOOLEAN_FLAGS = new Set([
|
|
44
|
-
"help",
|
|
45
|
-
"version",
|
|
46
|
-
"json"
|
|
47
|
-
]);
|
|
48
|
-
const parseCommand = (argv) => {
|
|
49
|
-
const { tokens } = parseArgs({
|
|
50
|
-
args: [...argv],
|
|
51
|
-
options: {
|
|
52
|
-
help: {
|
|
53
|
-
type: "boolean",
|
|
54
|
-
short: "h"
|
|
55
|
-
},
|
|
56
|
-
version: {
|
|
57
|
-
type: "boolean",
|
|
58
|
-
short: "v"
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
strict: false,
|
|
62
|
-
tokens: true
|
|
63
|
-
});
|
|
64
|
-
const flags = /* @__PURE__ */ new Map();
|
|
65
|
-
const repeated = /* @__PURE__ */ new Map();
|
|
66
|
-
const positionals = [];
|
|
67
|
-
let error;
|
|
68
|
-
for (let index = 0; index < tokens.length; index += 1) {
|
|
69
|
-
const token = tokens[index];
|
|
70
|
-
if (!token) break;
|
|
71
|
-
if (token.kind === "option-terminator") {
|
|
72
|
-
if (index + 1 < tokens.length) error ??= "siro takes no passthrough arguments after `--`.";
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
if (token.kind === "positional") {
|
|
76
|
-
positionals.push(token.value);
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
if (BOOLEAN_FLAGS.has(token.name)) if (token.value !== void 0) error ??= `Flag ${token.rawName} does not accept a value.`;
|
|
80
|
-
else {
|
|
81
|
-
if (token.name === "json" && flags.has("json")) error ??= "--json must be specified only once.";
|
|
82
|
-
flags.set(token.name, true);
|
|
83
|
-
}
|
|
84
|
-
else if (VALUE_FLAGS.has(token.name)) {
|
|
85
|
-
let value = token.value;
|
|
86
|
-
const next = tokens[index + 1];
|
|
87
|
-
if (value === void 0 && next?.kind === "positional" && next.index === token.index + 1) {
|
|
88
|
-
value = next.value;
|
|
89
|
-
index += 1;
|
|
90
|
-
}
|
|
91
|
-
if (value === void 0 || value === "") error ??= `${token.rawName} requires a value.`;
|
|
92
|
-
else if (REPEATABLE_FLAGS.has(token.name)) repeated.set(token.name, [...repeated.get(token.name) ?? [], value]);
|
|
93
|
-
else {
|
|
94
|
-
if (flags.has(token.name)) error ??= `${token.rawName} must be specified only once.`;
|
|
95
|
-
flags.set(token.name, value);
|
|
96
|
-
}
|
|
97
|
-
} 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}`;
|
|
98
|
-
}
|
|
99
|
-
const [command, cwd, ...extra] = positionals;
|
|
100
|
-
if (flags.has("help")) return {
|
|
101
|
-
kind: "help",
|
|
102
|
-
target: command && isCommandName(command) ? command : void 0
|
|
103
|
-
};
|
|
104
|
-
if (flags.has("version")) return { kind: "version" };
|
|
105
|
-
if (error) throw new UsageError(error);
|
|
106
|
-
if (command === void 0) return { kind: "usage" };
|
|
107
|
-
if (command === "init") return {
|
|
108
|
-
kind: "usage",
|
|
109
|
-
reason: "The 'init' command was removed: siro is lint-only. Run `siro lint --reporter json` and review each finding's remediation."
|
|
110
|
-
};
|
|
111
|
-
if (!isCommandName(command)) return {
|
|
112
|
-
kind: "usage",
|
|
113
|
-
reason: `Unknown command: ${command}`
|
|
114
|
-
};
|
|
115
|
-
if (extra.length > 0) throw new UsageError(`Unexpected extra argument: ${extra.join(" ")}`);
|
|
116
|
-
if (flags.has("reporter") && flags.has("json")) throw new UsageError("Invalid reporter selection: use either --reporter or --json.");
|
|
117
|
-
const reporter = flags.get("reporter");
|
|
118
|
-
const pmVersion = flags.get("pm-version");
|
|
119
|
-
return {
|
|
120
|
-
kind: "lint",
|
|
121
|
-
cwd: asAbsPath(path.resolve(cwd ?? process.cwd())),
|
|
122
|
-
pm: parsePmFlag(flags.get("pm")),
|
|
123
|
-
pmVersion: typeof pmVersion === "string" ? pmVersion : void 0,
|
|
124
|
-
exclude: repeated.get("exclude"),
|
|
125
|
-
installationRoots: repeated.get("installation-root"),
|
|
126
|
-
projectType: parseProjectTypeFlag(flags.get("project-type")),
|
|
127
|
-
severity: parseSeverityFlag(flags.get("severity")),
|
|
128
|
-
reporter: typeof reporter === "string" ? reporter : flags.has("json") ? JSON_REPORTER_NAME : DEFAULT_REPORTER_NAME
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
//#endregion
|
|
132
|
-
//#region src/cli/help.ts
|
|
133
|
-
const PMS_LIST = PMS.join("|");
|
|
134
|
-
const REPORTERS_LIST = BUILTIN_REPORTER_NAMES.join("|");
|
|
135
|
-
const PROJECT_TYPES_LIST = PROJECT_TYPES.join("|");
|
|
136
|
-
const SEVERITIES_LIST = SEVERITIES.join("|");
|
|
137
|
-
const FLAG_LINES = {
|
|
138
|
-
json: " --json Shortcut for --reporter json",
|
|
139
|
-
pm: ` --pm <name> Target a specific package manager (${PMS_LIST})`,
|
|
140
|
-
pmVersion: " --pm-version <x.y.z> Target an exact stable PM version (requires --pm)",
|
|
141
|
-
inspection: " --exclude <pattern> Exclude directories from recursive discovery (repeatable)\n --installation-root <path> Inspect local install policy here (repeatable; default .)",
|
|
142
|
-
projectType: ` --project-type <type> Project type (${PROJECT_TYPES_LIST}; default auto)`,
|
|
143
|
-
reporter: ` --reporter <name> Reporter (${REPORTERS_LIST}; additional reporters can be registered via siro.config.ts)`,
|
|
144
|
-
severity: ` --severity <level> Show + fail on findings at or above this level (${SEVERITIES_LIST})`
|
|
145
|
-
};
|
|
146
|
-
const HELP_ROOT = [
|
|
147
|
-
"siro — security best-practices for the npm ecosystem",
|
|
148
|
-
"",
|
|
149
|
-
"USAGE",
|
|
150
|
-
" siro <command> [path] [flags]",
|
|
151
|
-
"",
|
|
152
|
-
"COMMANDS",
|
|
153
|
-
" lint Report best-practice violations (alias: check)",
|
|
154
|
-
"",
|
|
155
|
-
"GLOBAL FLAGS",
|
|
156
|
-
FLAG_LINES.pm,
|
|
157
|
-
FLAG_LINES.pmVersion,
|
|
158
|
-
FLAG_LINES.inspection,
|
|
159
|
-
FLAG_LINES.projectType,
|
|
160
|
-
" --version Print the siro version",
|
|
161
|
-
" --help Show help for siro or a command",
|
|
162
|
-
"",
|
|
163
|
-
"LINT FLAGS",
|
|
164
|
-
FLAG_LINES.reporter,
|
|
165
|
-
FLAG_LINES.json,
|
|
166
|
-
FLAG_LINES.severity,
|
|
167
|
-
"",
|
|
168
|
-
"EXAMPLES",
|
|
169
|
-
" $ siro lint # report violations in cwd",
|
|
170
|
-
" $ siro lint --reporter github # GitHub Actions annotations",
|
|
171
|
-
" $ siro lint --severity warn # also fail on warnings",
|
|
172
|
-
"",
|
|
173
|
-
"LEARN MORE",
|
|
174
|
-
" Quickstart https://github.com/pHo9UBenaA/siro/blob/main/docs/getting-started.md",
|
|
175
|
-
" Rule reference https://github.com/pHo9UBenaA/siro/blob/main/docs/rules.md",
|
|
176
|
-
" Config options https://github.com/pHo9UBenaA/siro/blob/main/docs/configuration.md"
|
|
177
|
-
].join("\n");
|
|
178
|
-
const HELP_LINT = [
|
|
179
|
-
"siro lint — report best-practice violations",
|
|
180
|
-
"",
|
|
181
|
-
"USAGE",
|
|
182
|
-
" siro lint [path] [flags]",
|
|
183
|
-
" siro check [path] [flags] (alias)",
|
|
184
|
-
"",
|
|
185
|
-
"FLAGS",
|
|
186
|
-
FLAG_LINES.pm,
|
|
187
|
-
FLAG_LINES.pmVersion,
|
|
188
|
-
FLAG_LINES.inspection,
|
|
189
|
-
FLAG_LINES.projectType,
|
|
190
|
-
FLAG_LINES.reporter,
|
|
191
|
-
FLAG_LINES.json,
|
|
192
|
-
FLAG_LINES.severity,
|
|
193
|
-
"",
|
|
194
|
-
"EXIT CODES",
|
|
195
|
-
" 0 No findings at or above the active threshold",
|
|
196
|
-
" 1 Findings at or above the threshold (default: error)",
|
|
197
|
-
" 2 Usage error (bad flag, broken siro.config.ts, unreadable path, …)",
|
|
198
|
-
" 70 Uncaught exception (a siro bug, or a reporter / custom rule that threw)",
|
|
199
|
-
"",
|
|
200
|
-
"EXAMPLES",
|
|
201
|
-
" $ siro lint # default: pretty reporter, fail on errors",
|
|
202
|
-
" $ siro lint --reporter github # GitHub Actions annotations",
|
|
203
|
-
" $ siro lint --severity warn # tighten the gate"
|
|
204
|
-
].join("\n");
|
|
205
|
-
const renderHelp = (target) => {
|
|
206
|
-
if (typeof target === "undefined") return HELP_ROOT;
|
|
207
|
-
if (target === "lint" || target === "check") return HELP_LINT;
|
|
208
|
-
return target;
|
|
209
|
-
};
|
|
210
|
-
//#endregion
|
|
211
|
-
//#region src/cli.ts
|
|
212
|
-
const EXIT_SUCCESS = 0;
|
|
213
|
-
const EXIT_USAGE = 2;
|
|
214
|
-
const EXIT_CRASH = 70;
|
|
215
|
-
const dispatch = async (cmd, io) => {
|
|
216
|
-
switch (cmd.kind) {
|
|
217
|
-
case "version":
|
|
218
|
-
await io.stdout(version);
|
|
219
|
-
return EXIT_SUCCESS;
|
|
220
|
-
case "help":
|
|
221
|
-
await io.stdout(renderHelp(cmd.target));
|
|
222
|
-
return EXIT_SUCCESS;
|
|
223
|
-
case "usage":
|
|
224
|
-
if (cmd.reason) await io.stderr(`${safeText(cmd.reason)}\n`);
|
|
225
|
-
await io.stderr(renderHelp());
|
|
226
|
-
return EXIT_USAGE;
|
|
227
|
-
case "lint":
|
|
228
|
-
assertDirectory(cmd.cwd);
|
|
229
|
-
return loadConfig(cmd.cwd).then((config) => lintCommand({
|
|
230
|
-
...cmd,
|
|
231
|
-
config
|
|
232
|
-
}, io));
|
|
233
|
-
default: throw new Error(`Unhandled command kind: ${String(cmd)}`);
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
const handleError = async (error, io) => {
|
|
237
|
-
if (error instanceof SiroError) {
|
|
238
|
-
await io.stderr(safeText(error.message));
|
|
239
|
-
return error.exitCode;
|
|
240
|
-
}
|
|
241
|
-
if (isNodeError(error) && "errno" in error && typeof error.errno === "number") {
|
|
242
|
-
await io.stderr(safeText(`File system error: ${error.message}`));
|
|
243
|
-
return EXIT_USAGE;
|
|
244
|
-
}
|
|
245
|
-
throw error;
|
|
246
|
-
};
|
|
247
|
-
const run = async (argv, io = nodeIO) => {
|
|
248
|
-
try {
|
|
249
|
-
ensureNodeVersion(process.versions.node);
|
|
250
|
-
return await dispatch(parseCommand(argv), io);
|
|
251
|
-
} catch (error) {
|
|
252
|
-
return handleError(error, io);
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
const runMain = async (argv) => {
|
|
256
|
-
try {
|
|
257
|
-
process.exitCode = await run(argv);
|
|
258
|
-
} catch (error) {
|
|
259
|
-
const errStr = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
260
|
-
process.exitCode = EXIT_CRASH;
|
|
261
|
-
try {
|
|
262
|
-
await nodeIO.stderr(safeText(errStr));
|
|
263
|
-
} catch {}
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
const [, invokedPath] = process.argv;
|
|
267
|
-
if (invokedPath && import.meta.url === pathToFileURL(invokedPath).href) await runMain(process.argv.slice(2));
|
|
268
|
-
//#endregion
|
|
269
|
-
export { run, runMain };
|
|
270
|
-
|
|
2
|
+
import{C as isSeverity,D as asAbsPath,E as version,I as SiroError,L as UsageError,M as DEFAULT_SCAN_LIMITS,N as resolveScanLimits,P as isOwnKey,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-DE7Nzlal.mjs";import path from"node:path";import{pathToFileURL}from"node:url";import{parseArgs}from"node:util";const parsePmFlag=raw=>{if(typeof raw===`undefined`)return;if(typeof raw!==`string`||!isPM(raw))throw new UsageError(`Unknown package manager: ${raw} (expected one of: ${PMS.join(`, `)})`);return raw};const parseProjectTypeFlag=raw=>{if(typeof raw===`undefined`)return;if(typeof raw===`string`&&isProjectType(raw))return raw;throw new UsageError(`Unknown project type: ${String(raw)} (expected ${PROJECT_TYPES.join(`|`)})`)};const parseSeverityFlag=raw=>{if(typeof raw===`undefined`)return;if(typeof raw!==`string`||!isSeverity(raw))throw new UsageError(`Invalid severity: ${String(raw)} (expected ${SEVERITIES.join(`|`)})`);return raw};const ensureNodeVersion=nodeVersion=>{if(!isSupportedNodeVersion(nodeVersion))throw new UsageError(`Node ${SUPPORTED_NODE_RANGE} required (you are on ${nodeVersion}). Upgrade your runtime to use siro.`)};const COMMANDS=[`lint`,`check`];const isCommandName=value=>COMMANDS.some(command=>command===value);const options={maxFileBytes:{flag:`max-file-bytes`,description:`Bound each input file`},maxTotalBytes:{flag:`max-total-bytes`,description:`Bound all input reads`,descriptionColumn:25},maxEntries:{flag:`max-entries`,description:`Bound native directory entries`},maxDirectories:{flag:`max-directories`,description:`Bound discovered directories`},maxDirectoryDepth:{flag:`max-directory-depth`,description:`Bound discovery depth`},maxConfigDepth:{flag:`max-config-depth`,description:`Bound configuration nesting`},maxFindings:{flag:`max-findings`,description:`Bound unfiltered findings`},maxOutputBytes:{flag:`max-output-bytes`,description:`Bound report output`}};const limitOptions=[];for(const key of Object.keys(options)){if(!isOwnKey(options,key))continue;limitOptions.push({key,...options[key]})}const LIMIT_OPTIONS=limitOptions;const limitHelp=LIMIT_OPTIONS.map(({key,flag,description,descriptionColumn=24})=>`${` --${flag} <n> `.padEnd(descriptionColumn)}${description} (default ${DEFAULT_SCAN_LIMITS[key]})`).join(`
|
|
3
|
+
`);const REPEATABLE_FLAGS=new Set([`exclude`,`installation-root`]);const VALUE_FLAGS=new Set([`pm`,`pm-version`,`project-type`,`reporter`,`severity`,...REPEATABLE_FLAGS,...LIMIT_OPTIONS.map(({flag})=>flag)]);const BOOLEAN_FLAGS=new Set([`help`,`version`,`json`,`no-config`,`strict-filesystem`]);const collectArguments=argv=>{const{tokens}=parseArgs({args:[...argv],options:{help:{type:`boolean`,short:`h`},version:{type:`boolean`,short:`v`}},strict:false,tokens:true});const flags=new Set;const values=new Map;const repeatedFlags=new Map;const positionals=[];let firstError;const remainingTokens=tokens.entries();for(const[index,token]of remainingTokens){if(token.kind===`option-terminator`){if(index+1<tokens.length)firstError??="siro takes no passthrough arguments after `--`.";break}if(token.kind===`positional`){positionals.push(token.value);continue}if(BOOLEAN_FLAGS.has(token.name)){if(token.value!==void 0){firstError??=`Flag ${token.rawName} does not accept a value.`;continue}if([`json`,`no-config`,`strict-filesystem`].includes(token.name)&&flags.has(token.name))firstError??=`${token.rawName} must be specified only once.`;flags.add(token.name);continue}if(!VALUE_FLAGS.has(token.name)){firstError??=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}`;continue}const next=tokens[index+1];const consumesNext=token.value===void 0&&next?.kind===`positional`&&next.index===token.index+1;const value=consumesNext?next.value:token.value;if(consumesNext)remainingTokens.next();if(value===void 0||value===``){firstError??=`${token.rawName} requires a value.`;continue}if(REPEATABLE_FLAGS.has(token.name)){const occurrences=repeatedFlags.get(token.name)??[];occurrences.push(value);repeatedFlags.set(token.name,occurrences)}else{if(values.has(token.name))firstError??=`${token.rawName} must be specified only once.`;values.set(token.name,value)}}return{flags,values,repeatedFlags,positionals,firstError}};const parseLimitOverrides=values=>{const limits={};for(const{flag,key}of LIMIT_OPTIONS){const value=values.get(flag);if(value===void 0)continue;if(!/^\d+$/u.test(value))throw new UsageError(`${flag} must be a positive safe integer.`);limits[key]=Number(value)}resolveScanLimits(limits);return limits};const parseCommand=argv=>{const{flags,values,repeatedFlags,positionals,firstError}=collectArguments(argv);const[command,cwd,...extra]=positionals;if(flags.has(`help`))return{kind:`help`,target:command&&isCommandName(command)?command:void 0};if(flags.has(`version`))return{kind:`version`};if(firstError)throw new UsageError(firstError);if(command===void 0)return{kind:`usage`};if(command===`init`)return{kind:`usage`,reason:"The 'init' command was removed: siro is lint-only. Run `siro lint --reporter json` and review each finding's remediation."};if(!isCommandName(command))return{kind:`usage`,reason:`Unknown command: ${command}`};if(extra.length>0)throw new UsageError(`Unexpected extra argument: ${extra.join(` `)}`);if(values.has(`reporter`)&&flags.has(`json`))throw new UsageError(`Invalid reporter selection: use either --reporter or --json.`);const reporter=values.get(`reporter`)??(flags.has(`json`)?JSON_REPORTER_NAME:DEFAULT_REPORTER_NAME);const pmVersion=values.get(`pm-version`);const pm=parsePmFlag(values.get(`pm`));if(pmVersion!==void 0&&(!pm||!isStableVersion(pmVersion)))throw new UsageError(`--pm-version requires --pm and an exact stable version.`);const limits=parseLimitOverrides(values);return{kind:`lint`,cwd:asAbsPath(path.resolve(cwd??process.cwd())),pm,pmVersion,exclude:repeatedFlags.get(`exclude`),installationRoots:repeatedFlags.get(`installation-root`),projectType:parseProjectTypeFlag(values.get(`project-type`)),severity:parseSeverityFlag(values.get(`severity`)),...flags.has(`no-config`)?{noConfig:true}:{},...flags.has(`strict-filesystem`)?{rejectSymlinks:true}:{},...Object.keys(limits).length?{limits}:{},reporter}};const PMS_LIST=PMS.join(`|`);const REPORTERS_LIST=BUILTIN_REPORTER_NAMES.join(`|`);const PROJECT_TYPES_LIST=PROJECT_TYPES.join(`|`);const SEVERITIES_LIST=SEVERITIES.join(`|`);const FLAG_LINES={json:` --json Shortcut for --reporter json`,safety:` --no-config Do not probe or execute repository siro.config.*
|
|
4
|
+
--strict-filesystem Reject symlink input paths (not a containment sandbox)`,limits:limitHelp,pm:` --pm <name> Target a specific package manager (${PMS_LIST})`,pmVersion:` --pm-version <x.y.z> Target an exact stable PM version (requires --pm)`,inspection:` --exclude <pattern> Exclude directories from recursive discovery (repeatable)
|
|
5
|
+
--installation-root <path> Inspect local install policy here (repeatable; default .)`,projectType:` --project-type <type> Project type (${PROJECT_TYPES_LIST}; default auto)`,reporter:` --reporter <name> Reporter (${REPORTERS_LIST}; additional reporters can be registered via siro.config.ts)`,severity:` --severity <level> Show + fail on findings at or above this level (${SEVERITIES_LIST})`};const HELP_ROOT=[`siro — security best-practices for the npm ecosystem`,``,`USAGE`,` siro <command> [path] [flags]`,``,`COMMANDS`,` lint Report best-practice violations (alias: check)`,``,`GLOBAL FLAGS`,FLAG_LINES.pm,FLAG_LINES.pmVersion,FLAG_LINES.inspection,FLAG_LINES.projectType,` --version Print the siro version`,` --help Show help for siro or a command`,``,`LINT FLAGS`,FLAG_LINES.safety,FLAG_LINES.limits,FLAG_LINES.reporter,FLAG_LINES.json,FLAG_LINES.severity,``,`EXAMPLES`,` $ siro lint # report violations in cwd`,` $ siro lint --no-config --strict-filesystem # data-only inspection`,` $ siro lint --reporter github # GitHub Actions annotations`,` $ siro lint --severity warn # also fail on warnings`,``,`LEARN MORE`,` Quickstart https://github.com/pHo9UBenaA/siro/blob/main/docs/getting-started.md`,` Rule reference https://github.com/pHo9UBenaA/siro/blob/main/docs/rules.md`,` Config options https://github.com/pHo9UBenaA/siro/blob/main/docs/configuration.md`].join(`
|
|
6
|
+
`);const HELP_LINT=[`siro lint — report best-practice violations`,``,`USAGE`,` siro lint [path] [flags]`,` siro check [path] [flags] (alias)`,``,`FLAGS`,FLAG_LINES.safety,FLAG_LINES.limits,FLAG_LINES.pm,FLAG_LINES.pmVersion,FLAG_LINES.inspection,FLAG_LINES.projectType,FLAG_LINES.reporter,FLAG_LINES.json,FLAG_LINES.severity,``,`EXIT CODES`,` 0 No findings at or above the active threshold`,` 1 Findings at or above the threshold (default: error)`,` 2 Usage error (bad flag, broken siro.config.ts, unreadable path, …)`,` 70 Output failure/limit, or unexpected exception (including trusted extensions)`,``,`EXAMPLES`,` $ siro lint # default: pretty reporter, fail on errors`,` $ siro lint --no-config --strict-filesystem # data-only inspection`,` $ siro lint --reporter github # GitHub Actions annotations`,` $ siro lint --severity warn # tighten the gate`].join(`
|
|
7
|
+
`);const renderHelp=target=>{if(typeof target===`undefined`)return HELP_ROOT;if(target===`lint`||target===`check`)return HELP_LINT;return target};const EXIT_SUCCESS=0;const EXIT_USAGE=2;const EXIT_CRASH=70;const dispatch=async(command,io)=>{switch(command.kind){case`version`:await io.stdout(version);return EXIT_SUCCESS;case`help`:await io.stdout(renderHelp(command.target));return EXIT_SUCCESS;case`usage`:if(command.reason)await io.stderr(`${safeText(command.reason)}\n`);await io.stderr(renderHelp());return EXIT_USAGE;case`lint`:{assertDirectory(command.cwd);const{noConfig,...options}=command;if(noConfig)return lintCommand(options,io);const config=await loadConfig(command.cwd);return lintCommand({...options,config},io)}default:throw new Error(`Unhandled command kind: ${String(command)}`)}};const handleError=async(error,io)=>{if(error instanceof SiroError){await io.stderr(safeText(error.message));return error.exitCode}if(isNodeError(error)&&`errno`in error&&typeof error.errno===`number`){await io.stderr(safeText(`File system error: ${error.message}`));return EXIT_USAGE}throw error};const run=async(argv,io=nodeIO)=>{try{ensureNodeVersion(process.versions.node);return await dispatch(parseCommand(argv),io)}catch(error){return handleError(error,io)}};const runMain=async argv=>{try{process.exitCode=await run(argv)}catch(error){const diagnostic=error instanceof Error?error.stack??error.message:String(error);process.exitCode=EXIT_CRASH;try{await nodeIO.stderr(safeText(diagnostic))}catch{}}};const[,invokedPath]=process.argv;if(invokedPath&&import.meta.url===pathToFileURL(invokedPath).href)await runMain(process.argv.slice(2));export{run,runMain};
|
|
271
8
|
//# sourceMappingURL=cli.mjs.map
|
package/dist/index.d.mts
CHANGED
|
@@ -35,12 +35,7 @@ interface ConfigFileRef {
|
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
37
|
//#region src/core/contracts/pms.d.ts
|
|
38
|
-
/**
|
|
39
|
-
* Single source of truth for package managers and severities.
|
|
40
|
-
* Types, runtime sets, and ordering derive from these tuples. Adding a manager
|
|
41
|
-
* still requires local detection signals, applicable rule bindings and verified
|
|
42
|
-
* policy/availability evidence; the tuple alone does not implement support.
|
|
43
|
-
*/
|
|
38
|
+
/** Supported identifiers in stable evaluation/display order. */
|
|
44
39
|
declare const PMS: readonly ["npm", "pnpm", "yarn", "bun", "deno", "aube"];
|
|
45
40
|
type PM = (typeof PMS)[number];
|
|
46
41
|
declare const SEVERITIES: readonly ["error", "warn", "info"];
|
|
@@ -146,7 +141,7 @@ interface Rule<Id extends string = string> {
|
|
|
146
141
|
declare const defineRule: <const Id extends string>(rule: Rule<Id>) => Rule<Id>;
|
|
147
142
|
//#endregion
|
|
148
143
|
//#region src/core/contracts/file-system.d.ts
|
|
149
|
-
/**
|
|
144
|
+
/** Read-only filesystem boundary for repository inspection. */
|
|
150
145
|
interface FileSystem {
|
|
151
146
|
/** Ordinary native child directory names, excluding symlinks. Required for recursive discovery. Errors must propagate. */
|
|
152
147
|
readDirectories: (path: AbsPath) => readonly string[];
|
|
@@ -201,9 +196,24 @@ interface LintResult {
|
|
|
201
196
|
readonly inspection: Inspection;
|
|
202
197
|
}
|
|
203
198
|
//#endregion
|
|
199
|
+
//#region src/core/contracts/scan-limits.d.ts
|
|
200
|
+
/** Finite per-scan budgets, controlled by the caller, never repository config. */
|
|
201
|
+
interface ScanLimits {
|
|
202
|
+
readonly maxFileBytes: number;
|
|
203
|
+
readonly maxTotalBytes: number;
|
|
204
|
+
readonly maxEntries: number;
|
|
205
|
+
readonly maxDirectories: number;
|
|
206
|
+
readonly maxDirectoryDepth: number;
|
|
207
|
+
readonly maxConfigDepth: number;
|
|
208
|
+
readonly maxFindings: number;
|
|
209
|
+
readonly maxOutputBytes: number;
|
|
210
|
+
}
|
|
211
|
+
declare const DEFAULT_SCAN_LIMITS: Readonly<ScanLimits>;
|
|
212
|
+
//#endregion
|
|
204
213
|
//#region src/core/contracts/reporter.d.ts
|
|
205
214
|
interface ReportContext {
|
|
206
215
|
readonly cwd: AbsPath;
|
|
216
|
+
readonly limits?: ScanLimits;
|
|
207
217
|
}
|
|
208
218
|
interface Reporter<Name extends string = string> {
|
|
209
219
|
readonly name: Name;
|
|
@@ -220,7 +230,9 @@ type InstallationRootInput = string | {
|
|
|
220
230
|
//#region src/core/contracts/date-time.d.ts
|
|
221
231
|
/** Host time and native date parsing, including the host timezone for offsetless input. */
|
|
222
232
|
interface DateTime {
|
|
233
|
+
/** Current epoch milliseconds. */
|
|
223
234
|
now: () => number;
|
|
235
|
+
/** Epoch milliseconds, or NaN for invalid input; offsetless dates use host semantics. */
|
|
224
236
|
parse: (value: string) => number;
|
|
225
237
|
}
|
|
226
238
|
//#endregion
|
|
@@ -260,6 +272,10 @@ declare const defineConfig: (config: SiroConfig) => SiroConfig;
|
|
|
260
272
|
interface LintOptions {
|
|
261
273
|
readonly cwd: AbsPath;
|
|
262
274
|
readonly fs?: FileSystem;
|
|
275
|
+
/** Caller-controlled finite budgets; omitted values use DEFAULT_SCAN_LIMITS. */
|
|
276
|
+
readonly limits?: Partial<ScanLimits>;
|
|
277
|
+
/** Reject file/path symlinks in the native adapter; not an atomic containment sandbox. */
|
|
278
|
+
readonly rejectSymlinks?: boolean;
|
|
263
279
|
/** Applies only to cwd, never to discovered children or additional installation roots. */
|
|
264
280
|
readonly pm?: PM;
|
|
265
281
|
readonly pmVersion?: string;
|
|
@@ -282,32 +298,13 @@ interface LintCommandOptions extends LintOptions {
|
|
|
282
298
|
declare const lint: (options: LintOptions) => LintResult;
|
|
283
299
|
declare const lintCommand: (options: LintCommandOptions, io: IO) => Promise<number>;
|
|
284
300
|
//#endregion
|
|
301
|
+
//#region src/version.d.ts
|
|
302
|
+
declare const version: string;
|
|
303
|
+
//#endregion
|
|
285
304
|
//#region src/load-config.d.ts
|
|
286
305
|
/** Load the first matching config; executable imports always use the real filesystem. */
|
|
287
306
|
declare const loadConfig: (cwd: AbsPath, nodeVersion?: string) => Promise<SiroConfig | undefined>;
|
|
288
307
|
//#endregion
|
|
289
|
-
//#region src/adapters/node-file-system.d.ts
|
|
290
|
-
declare const nodeFileSystem: FileSystem;
|
|
291
|
-
//#endregion
|
|
292
|
-
//#region src/adapters/node-io.d.ts
|
|
293
|
-
declare const nodeIO: IO;
|
|
294
|
-
//#endregion
|
|
295
|
-
//#region src/adapters/reporters/github.d.ts
|
|
296
|
-
/** Emit GitHub Actions workflow commands (annotations on PRs). */
|
|
297
|
-
declare const githubReporter: Reporter<'github'>;
|
|
298
|
-
//#endregion
|
|
299
|
-
//#region src/adapters/reporters/json.d.ts
|
|
300
|
-
declare const jsonReporter: Reporter<'json'>;
|
|
301
|
-
//#endregion
|
|
302
|
-
//#region src/adapters/reporters/pretty.d.ts
|
|
303
|
-
declare const prettyReporter: Reporter<'pretty'>;
|
|
304
|
-
//#endregion
|
|
305
|
-
//#region src/adapters/reporters/registry.d.ts
|
|
306
|
-
/** Names of every built-in reporter, in stable display order. */
|
|
307
|
-
declare const BUILTIN_REPORTER_NAMES: readonly BuiltinReporterName[];
|
|
308
|
-
/** Literal union of every built-in reporter name. */
|
|
309
|
-
type BuiltinReporterName = (typeof prettyReporter | typeof jsonReporter | typeof githubReporter)['name'];
|
|
310
|
-
//#endregion
|
|
311
308
|
//#region src/core/config-files.d.ts
|
|
312
309
|
/** Known configuration locations and their parsers. */
|
|
313
310
|
declare const CONFIG_FILES: {
|
|
@@ -357,7 +354,7 @@ interface RequireConfigKeySpec {
|
|
|
357
354
|
/** Only unconditional defaults may reduce severity; omitted means conditional. */
|
|
358
355
|
readonly defaultSafety?: 'unconditional' | 'conditional';
|
|
359
356
|
/**
|
|
360
|
-
* Severity
|
|
357
|
+
* Severity when an unconditional `documentedDefault` satisfies the requirement. Defaults
|
|
361
358
|
* to `'info'` (advisory). Set `'off'` to silence the finding entirely.
|
|
362
359
|
*/
|
|
363
360
|
readonly defaultSatisfiedSeverity?: Severity | 'off';
|
|
@@ -379,6 +376,28 @@ declare const overrideBindings: <Id extends string>(rule: Rule<Id>, overrides: P
|
|
|
379
376
|
/** Build a Rule from a per-PM table of {file, keyPath, value, message}. */
|
|
380
377
|
declare const requireConfigKey: <const Id extends string>(options: RequireConfigKeyOptions<Id>) => Rule<Id>;
|
|
381
378
|
//#endregion
|
|
379
|
+
//#region src/adapters/node-file-system.d.ts
|
|
380
|
+
declare const nodeFileSystem: FileSystem;
|
|
381
|
+
//#endregion
|
|
382
|
+
//#region src/adapters/node-io.d.ts
|
|
383
|
+
declare const nodeIO: IO;
|
|
384
|
+
//#endregion
|
|
385
|
+
//#region src/adapters/reporters/github.d.ts
|
|
386
|
+
/** Emit GitHub Actions workflow commands (annotations on PRs). */
|
|
387
|
+
declare const githubReporter: Reporter<'github'>;
|
|
388
|
+
//#endregion
|
|
389
|
+
//#region src/adapters/reporters/json.d.ts
|
|
390
|
+
declare const jsonReporter: Reporter<'json'>;
|
|
391
|
+
//#endregion
|
|
392
|
+
//#region src/adapters/reporters/pretty.d.ts
|
|
393
|
+
declare const prettyReporter: Reporter<'pretty'>;
|
|
394
|
+
//#endregion
|
|
395
|
+
//#region src/adapters/reporters/registry.d.ts
|
|
396
|
+
/** Names of every built-in reporter, in stable display order. */
|
|
397
|
+
declare const BUILTIN_REPORTER_NAMES: readonly BuiltinReporterName[];
|
|
398
|
+
/** Literal union of every built-in reporter name. */
|
|
399
|
+
type BuiltinReporterName = (typeof prettyReporter | typeof jsonReporter | typeof githubReporter)['name'];
|
|
400
|
+
//#endregion
|
|
382
401
|
//#region src/core/contracts/errors.d.ts
|
|
383
402
|
declare class SiroError extends Error {
|
|
384
403
|
readonly exitCode: number;
|
|
@@ -391,11 +410,8 @@ declare class UsageError extends SiroError {
|
|
|
391
410
|
constructor(message: string);
|
|
392
411
|
}
|
|
393
412
|
//#endregion
|
|
394
|
-
//#region src/version.d.ts
|
|
395
|
-
declare const version: string;
|
|
396
|
-
//#endregion
|
|
397
413
|
//#region src/adapters/node-paths.d.ts
|
|
398
414
|
declare const asAbsPath: (value: string) => AbsPath;
|
|
399
415
|
//#endregion
|
|
400
|
-
export { type AbsPath, BUILTIN_REPORTER_NAMES, type BuiltinReporterName, CONFIG_FILES, type CheckStatus, ConfigError, type ConfigFileRef, type ConfigReadValue, type ConfigValue, type FileSystem, type Finding, type IO, type Inspection, type InstallationRootInput, type KeyPath, type LintCommandOptions, type LintOptions, type LintResult, type PM, PMS, PROJECT_TYPES, type ParsedConfig, type PolicyTarget, type ProjectType, type RelPath, type Remediation, type RepoContext, type ReportContext, type Reporter, type RequireConfigKeyOptions, type RequireConfigKeySpec, type Rule, type RuleBinding, type RuleContext, type RuleSetting, SEVERITIES, type SetKeyOperation, type Severity, type SiroConfig, SiroError, UsageError, type VersionNote, type ViolationStatus, asAbsPath, asRelPath, defineConfig, defineRule, getByPath, githubReporter, isPM, isSeverity, jsonReporter, lint, lintCommand, loadConfig, nodeFileSystem, nodeIO, overrideBindings, prettyReporter, requireConfigKey, version };
|
|
416
|
+
export { type AbsPath, BUILTIN_REPORTER_NAMES, type BuiltinReporterName, CONFIG_FILES, type CheckStatus, ConfigError, type ConfigFileRef, type ConfigReadValue, type ConfigValue, DEFAULT_SCAN_LIMITS, type FileSystem, type Finding, type IO, type Inspection, type InstallationRootInput, type KeyPath, type LintCommandOptions, type LintOptions, type LintResult, type PM, PMS, PROJECT_TYPES, type ParsedConfig, type PolicyTarget, type ProjectType, type RelPath, type Remediation, type RepoContext, type ReportContext, type Reporter, type RequireConfigKeyOptions, type RequireConfigKeySpec, type Rule, type RuleBinding, type RuleContext, type RuleSetting, SEVERITIES, type ScanLimits, type SetKeyOperation, type Severity, type SiroConfig, SiroError, UsageError, type VersionNote, type ViolationStatus, asAbsPath, asRelPath, defineConfig, defineRule, getByPath, githubReporter, isPM, isSeverity, jsonReporter, lint, lintCommand, loadConfig, nodeFileSystem, nodeIO, overrideBindings, prettyReporter, requireConfigKey, version };
|
|
401
417
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
//#region src/core/siro-config.ts
|
|
3
|
-
/** Identity helper for type-checked config files. */
|
|
4
|
-
const defineConfig = (config) => config;
|
|
5
|
-
//#endregion
|
|
6
|
-
export { BUILTIN_REPORTER_NAMES, CONFIG_FILES, ConfigError, PMS, PROJECT_TYPES, SEVERITIES, SiroError, UsageError, asAbsPath, asRelPath, defineConfig, defineRule, getByPath, githubReporter, isPM, isSeverity, jsonReporter, lint, lintCommand, loadConfig, nodeFileSystem, nodeIO, overrideBindings, prettyReporter, requireConfigKey, version };
|
|
7
|
-
|
|
1
|
+
import{A as nodeFileSystem,C as isSeverity,D as asAbsPath,E as version,F as ConfigError,I as SiroError,L as UsageError,M as DEFAULT_SCAN_LIMITS,O as asRelPath,S as isPM,_ as PROJECT_TYPES,b as PMS,c as jsonReporter,d as overrideBindings,f as requireConfigKey,g as getByPath,h as defineRule,i as BUILTIN_REPORTER_NAMES,m as loadConfig,n as lint,p as CONFIG_FILES,r as lintCommand,s as prettyReporter,t as nodeIO,u as githubReporter,x as SEVERITIES}from"./node-io-DE7Nzlal.mjs";const defineConfig=config=>config;export{BUILTIN_REPORTER_NAMES,CONFIG_FILES,ConfigError,DEFAULT_SCAN_LIMITS,PMS,PROJECT_TYPES,SEVERITIES,SiroError,UsageError,asAbsPath,asRelPath,defineConfig,defineRule,getByPath,githubReporter,isPM,isSeverity,jsonReporter,lint,lintCommand,loadConfig,nodeFileSystem,nodeIO,overrideBindings,prettyReporter,requireConfigKey,version};
|
|
8
2
|
//# sourceMappingURL=index.mjs.map
|