@pho9ubenaa/siro 0.6.1 → 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 +10 -0
- package/dist/cli.mjs +6 -303
- package/dist/index.d.mts +30 -33
- package/dist/index.mjs +1 -7
- package/dist/node-io-DE7Nzlal.mjs +7 -0
- package/package.json +1 -1
- package/dist/node-io-BpAHyeBG.mjs +0 -3347
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
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
|
+
|
|
3
13
|
## [0.6.1] — 2026-10-02
|
|
4
14
|
|
|
5
15
|
- Add `--no-config` to skip executable repository configuration, and
|
package/dist/cli.mjs
CHANGED
|
@@ -1,305 +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 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"]);
|
|
36
|
-
const VALUE_FLAGS = new Set([
|
|
37
|
-
"pm",
|
|
38
|
-
"pm-version",
|
|
39
|
-
"project-type",
|
|
40
|
-
"reporter",
|
|
41
|
-
"severity",
|
|
42
|
-
...REPEATABLE_FLAGS,
|
|
43
|
-
...LIMIT_FLAGS.keys()
|
|
44
|
-
]);
|
|
45
|
-
const BOOLEAN_FLAGS = new Set([
|
|
46
|
-
"help",
|
|
47
|
-
"version",
|
|
48
|
-
"json",
|
|
49
|
-
"no-config",
|
|
50
|
-
"strict-filesystem"
|
|
51
|
-
]);
|
|
52
|
-
const parseCommand = (argv) => {
|
|
53
|
-
const { tokens } = parseArgs({
|
|
54
|
-
args: [...argv],
|
|
55
|
-
options: {
|
|
56
|
-
help: {
|
|
57
|
-
type: "boolean",
|
|
58
|
-
short: "h"
|
|
59
|
-
},
|
|
60
|
-
version: {
|
|
61
|
-
type: "boolean",
|
|
62
|
-
short: "v"
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
strict: false,
|
|
66
|
-
tokens: true
|
|
67
|
-
});
|
|
68
|
-
const flags = /* @__PURE__ */ new Map();
|
|
69
|
-
const repeated = /* @__PURE__ */ new Map();
|
|
70
|
-
const positionals = [];
|
|
71
|
-
let error;
|
|
72
|
-
for (let index = 0; index < tokens.length; index += 1) {
|
|
73
|
-
const token = tokens[index];
|
|
74
|
-
if (!token) break;
|
|
75
|
-
if (token.kind === "option-terminator") {
|
|
76
|
-
if (index + 1 < tokens.length) error ??= "siro takes no passthrough arguments after `--`.";
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
if (token.kind === "positional") {
|
|
80
|
-
positionals.push(token.value);
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if (BOOLEAN_FLAGS.has(token.name)) if (token.value !== void 0) error ??= `Flag ${token.rawName} does not accept a value.`;
|
|
84
|
-
else {
|
|
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.`;
|
|
90
|
-
flags.set(token.name, true);
|
|
91
|
-
}
|
|
92
|
-
else if (VALUE_FLAGS.has(token.name)) {
|
|
93
|
-
let value = token.value;
|
|
94
|
-
const next = tokens[index + 1];
|
|
95
|
-
if (value === void 0 && next?.kind === "positional" && next.index === token.index + 1) {
|
|
96
|
-
value = next.value;
|
|
97
|
-
index += 1;
|
|
98
|
-
}
|
|
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]);
|
|
101
|
-
else {
|
|
102
|
-
if (flags.has(token.name)) error ??= `${token.rawName} must be specified only once.`;
|
|
103
|
-
flags.set(token.name, value);
|
|
104
|
-
}
|
|
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}`;
|
|
106
|
-
}
|
|
107
|
-
const [command, cwd, ...extra] = positionals;
|
|
108
|
-
if (flags.has("help")) return {
|
|
109
|
-
kind: "help",
|
|
110
|
-
target: command && isCommandName(command) ? command : void 0
|
|
111
|
-
};
|
|
112
|
-
if (flags.has("version")) return { kind: "version" };
|
|
113
|
-
if (error) throw new UsageError(error);
|
|
114
|
-
if (command === void 0) return { kind: "usage" };
|
|
115
|
-
if (command === "init") return {
|
|
116
|
-
kind: "usage",
|
|
117
|
-
reason: "The 'init' command was removed: siro is lint-only. Run `siro lint --reporter json` and review each finding's remediation."
|
|
118
|
-
};
|
|
119
|
-
if (!isCommandName(command)) return {
|
|
120
|
-
kind: "usage",
|
|
121
|
-
reason: `Unknown command: ${command}`
|
|
122
|
-
};
|
|
123
|
-
if (extra.length > 0) throw new UsageError(`Unexpected extra argument: ${extra.join(" ")}`);
|
|
124
|
-
if (flags.has("reporter") && flags.has("json")) throw new UsageError("Invalid reporter selection: use either --reporter or --json.");
|
|
125
|
-
const reporter = flags.get("reporter");
|
|
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);
|
|
138
|
-
return {
|
|
139
|
-
kind: "lint",
|
|
140
|
-
cwd: asAbsPath(path.resolve(cwd ?? process.cwd())),
|
|
141
|
-
pm,
|
|
142
|
-
pmVersion: typeof pmVersion === "string" ? pmVersion : void 0,
|
|
143
|
-
exclude: repeated.get("exclude"),
|
|
144
|
-
installationRoots: repeated.get("installation-root"),
|
|
145
|
-
projectType: parseProjectTypeFlag(flags.get("project-type")),
|
|
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 } : {},
|
|
150
|
-
reporter: typeof reporter === "string" ? reporter : flags.has("json") ? JSON_REPORTER_NAME : DEFAULT_REPORTER_NAME
|
|
151
|
-
};
|
|
152
|
-
};
|
|
153
|
-
//#endregion
|
|
154
|
-
//#region src/cli/help.ts
|
|
155
|
-
const PMS_LIST = PMS.join("|");
|
|
156
|
-
const REPORTERS_LIST = BUILTIN_REPORTER_NAMES.join("|");
|
|
157
|
-
const PROJECT_TYPES_LIST = PROJECT_TYPES.join("|");
|
|
158
|
-
const SEVERITIES_LIST = SEVERITIES.join("|");
|
|
159
|
-
const FLAG_LINES = {
|
|
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)",
|
|
163
|
-
pm: ` --pm <name> Target a specific package manager (${PMS_LIST})`,
|
|
164
|
-
pmVersion: " --pm-version <x.y.z> Target an exact stable PM version (requires --pm)",
|
|
165
|
-
inspection: " --exclude <pattern> Exclude directories from recursive discovery (repeatable)\n --installation-root <path> Inspect local install policy here (repeatable; default .)",
|
|
166
|
-
projectType: ` --project-type <type> Project type (${PROJECT_TYPES_LIST}; default auto)`,
|
|
167
|
-
reporter: ` --reporter <name> Reporter (${REPORTERS_LIST}; additional reporters can be registered via siro.config.ts)`,
|
|
168
|
-
severity: ` --severity <level> Show + fail on findings at or above this level (${SEVERITIES_LIST})`
|
|
169
|
-
};
|
|
170
|
-
const HELP_ROOT = [
|
|
171
|
-
"siro — security best-practices for the npm ecosystem",
|
|
172
|
-
"",
|
|
173
|
-
"USAGE",
|
|
174
|
-
" siro <command> [path] [flags]",
|
|
175
|
-
"",
|
|
176
|
-
"COMMANDS",
|
|
177
|
-
" lint Report best-practice violations (alias: check)",
|
|
178
|
-
"",
|
|
179
|
-
"GLOBAL FLAGS",
|
|
180
|
-
FLAG_LINES.pm,
|
|
181
|
-
FLAG_LINES.pmVersion,
|
|
182
|
-
FLAG_LINES.inspection,
|
|
183
|
-
FLAG_LINES.projectType,
|
|
184
|
-
" --version Print the siro version",
|
|
185
|
-
" --help Show help for siro or a command",
|
|
186
|
-
"",
|
|
187
|
-
"LINT FLAGS",
|
|
188
|
-
FLAG_LINES.safety,
|
|
189
|
-
FLAG_LINES.limits,
|
|
190
|
-
FLAG_LINES.reporter,
|
|
191
|
-
FLAG_LINES.json,
|
|
192
|
-
FLAG_LINES.severity,
|
|
193
|
-
"",
|
|
194
|
-
"EXAMPLES",
|
|
195
|
-
" $ siro lint # report violations in cwd",
|
|
196
|
-
" $ siro lint --no-config --strict-filesystem # data-only inspection",
|
|
197
|
-
" $ siro lint --reporter github # GitHub Actions annotations",
|
|
198
|
-
" $ siro lint --severity warn # also fail on warnings",
|
|
199
|
-
"",
|
|
200
|
-
"LEARN MORE",
|
|
201
|
-
" Quickstart https://github.com/pHo9UBenaA/siro/blob/main/docs/getting-started.md",
|
|
202
|
-
" Rule reference https://github.com/pHo9UBenaA/siro/blob/main/docs/rules.md",
|
|
203
|
-
" Config options https://github.com/pHo9UBenaA/siro/blob/main/docs/configuration.md"
|
|
204
|
-
].join("\n");
|
|
205
|
-
const HELP_LINT = [
|
|
206
|
-
"siro lint — report best-practice violations",
|
|
207
|
-
"",
|
|
208
|
-
"USAGE",
|
|
209
|
-
" siro lint [path] [flags]",
|
|
210
|
-
" siro check [path] [flags] (alias)",
|
|
211
|
-
"",
|
|
212
|
-
"FLAGS",
|
|
213
|
-
FLAG_LINES.safety,
|
|
214
|
-
FLAG_LINES.limits,
|
|
215
|
-
FLAG_LINES.pm,
|
|
216
|
-
FLAG_LINES.pmVersion,
|
|
217
|
-
FLAG_LINES.inspection,
|
|
218
|
-
FLAG_LINES.projectType,
|
|
219
|
-
FLAG_LINES.reporter,
|
|
220
|
-
FLAG_LINES.json,
|
|
221
|
-
FLAG_LINES.severity,
|
|
222
|
-
"",
|
|
223
|
-
"EXIT CODES",
|
|
224
|
-
" 0 No findings at or above the active threshold",
|
|
225
|
-
" 1 Findings at or above the threshold (default: error)",
|
|
226
|
-
" 2 Usage error (bad flag, broken siro.config.ts, unreadable path, …)",
|
|
227
|
-
" 70 Output failure/limit, or unexpected exception (including trusted extensions)",
|
|
228
|
-
"",
|
|
229
|
-
"EXAMPLES",
|
|
230
|
-
" $ siro lint # default: pretty reporter, fail on errors",
|
|
231
|
-
" $ siro lint --no-config --strict-filesystem # data-only inspection",
|
|
232
|
-
" $ siro lint --reporter github # GitHub Actions annotations",
|
|
233
|
-
" $ siro lint --severity warn # tighten the gate"
|
|
234
|
-
].join("\n");
|
|
235
|
-
const renderHelp = (target) => {
|
|
236
|
-
if (typeof target === "undefined") return HELP_ROOT;
|
|
237
|
-
if (target === "lint" || target === "check") return HELP_LINT;
|
|
238
|
-
return target;
|
|
239
|
-
};
|
|
240
|
-
//#endregion
|
|
241
|
-
//#region src/cli.ts
|
|
242
|
-
const EXIT_SUCCESS = 0;
|
|
243
|
-
const EXIT_USAGE = 2;
|
|
244
|
-
const EXIT_CRASH = 70;
|
|
245
|
-
const dispatch = async (cmd, io) => {
|
|
246
|
-
switch (cmd.kind) {
|
|
247
|
-
case "version":
|
|
248
|
-
await io.stdout(version);
|
|
249
|
-
return EXIT_SUCCESS;
|
|
250
|
-
case "help":
|
|
251
|
-
await io.stdout(renderHelp(cmd.target));
|
|
252
|
-
return EXIT_SUCCESS;
|
|
253
|
-
case "usage":
|
|
254
|
-
if (cmd.reason) await io.stderr(`${safeText(cmd.reason)}\n`);
|
|
255
|
-
await io.stderr(renderHelp());
|
|
256
|
-
return EXIT_USAGE;
|
|
257
|
-
case "lint": {
|
|
258
|
-
assertDirectory(cmd.cwd);
|
|
259
|
-
const { noConfig, ...options } = cmd;
|
|
260
|
-
if (noConfig) return lintCommand(options, io);
|
|
261
|
-
const config = await loadConfig(cmd.cwd);
|
|
262
|
-
return lintCommand({
|
|
263
|
-
...options,
|
|
264
|
-
config
|
|
265
|
-
}, io);
|
|
266
|
-
}
|
|
267
|
-
default: throw new Error(`Unhandled command kind: ${String(cmd)}`);
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
const handleError = async (error, io) => {
|
|
271
|
-
if (error instanceof SiroError) {
|
|
272
|
-
await io.stderr(safeText(error.message));
|
|
273
|
-
return error.exitCode;
|
|
274
|
-
}
|
|
275
|
-
if (isNodeError(error) && "errno" in error && typeof error.errno === "number") {
|
|
276
|
-
await io.stderr(safeText(`File system error: ${error.message}`));
|
|
277
|
-
return EXIT_USAGE;
|
|
278
|
-
}
|
|
279
|
-
throw error;
|
|
280
|
-
};
|
|
281
|
-
const run = async (argv, io = nodeIO) => {
|
|
282
|
-
try {
|
|
283
|
-
ensureNodeVersion(process.versions.node);
|
|
284
|
-
return await dispatch(parseCommand(argv), io);
|
|
285
|
-
} catch (error) {
|
|
286
|
-
return handleError(error, io);
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
const runMain = async (argv) => {
|
|
290
|
-
try {
|
|
291
|
-
process.exitCode = await run(argv);
|
|
292
|
-
} catch (error) {
|
|
293
|
-
const errStr = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
294
|
-
process.exitCode = EXIT_CRASH;
|
|
295
|
-
try {
|
|
296
|
-
await nodeIO.stderr(safeText(errStr));
|
|
297
|
-
} catch {}
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
const [, invokedPath] = process.argv;
|
|
301
|
-
if (invokedPath && import.meta.url === pathToFileURL(invokedPath).href) await runMain(process.argv.slice(2));
|
|
302
|
-
//#endregion
|
|
303
|
-
export { run, runMain };
|
|
304
|
-
|
|
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};
|
|
305
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[];
|
|
@@ -235,7 +230,9 @@ type InstallationRootInput = string | {
|
|
|
235
230
|
//#region src/core/contracts/date-time.d.ts
|
|
236
231
|
/** Host time and native date parsing, including the host timezone for offsetless input. */
|
|
237
232
|
interface DateTime {
|
|
233
|
+
/** Current epoch milliseconds. */
|
|
238
234
|
now: () => number;
|
|
235
|
+
/** Epoch milliseconds, or NaN for invalid input; offsetless dates use host semantics. */
|
|
239
236
|
parse: (value: string) => number;
|
|
240
237
|
}
|
|
241
238
|
//#endregion
|
|
@@ -301,32 +298,13 @@ interface LintCommandOptions extends LintOptions {
|
|
|
301
298
|
declare const lint: (options: LintOptions) => LintResult;
|
|
302
299
|
declare const lintCommand: (options: LintCommandOptions, io: IO) => Promise<number>;
|
|
303
300
|
//#endregion
|
|
301
|
+
//#region src/version.d.ts
|
|
302
|
+
declare const version: string;
|
|
303
|
+
//#endregion
|
|
304
304
|
//#region src/load-config.d.ts
|
|
305
305
|
/** Load the first matching config; executable imports always use the real filesystem. */
|
|
306
306
|
declare const loadConfig: (cwd: AbsPath, nodeVersion?: string) => Promise<SiroConfig | undefined>;
|
|
307
307
|
//#endregion
|
|
308
|
-
//#region src/adapters/node-file-system.d.ts
|
|
309
|
-
declare const nodeFileSystem: FileSystem;
|
|
310
|
-
//#endregion
|
|
311
|
-
//#region src/adapters/node-io.d.ts
|
|
312
|
-
declare const nodeIO: IO;
|
|
313
|
-
//#endregion
|
|
314
|
-
//#region src/adapters/reporters/github.d.ts
|
|
315
|
-
/** Emit GitHub Actions workflow commands (annotations on PRs). */
|
|
316
|
-
declare const githubReporter: Reporter<'github'>;
|
|
317
|
-
//#endregion
|
|
318
|
-
//#region src/adapters/reporters/json.d.ts
|
|
319
|
-
declare const jsonReporter: Reporter<'json'>;
|
|
320
|
-
//#endregion
|
|
321
|
-
//#region src/adapters/reporters/pretty.d.ts
|
|
322
|
-
declare const prettyReporter: Reporter<'pretty'>;
|
|
323
|
-
//#endregion
|
|
324
|
-
//#region src/adapters/reporters/registry.d.ts
|
|
325
|
-
/** Names of every built-in reporter, in stable display order. */
|
|
326
|
-
declare const BUILTIN_REPORTER_NAMES: readonly BuiltinReporterName[];
|
|
327
|
-
/** Literal union of every built-in reporter name. */
|
|
328
|
-
type BuiltinReporterName = (typeof prettyReporter | typeof jsonReporter | typeof githubReporter)['name'];
|
|
329
|
-
//#endregion
|
|
330
308
|
//#region src/core/config-files.d.ts
|
|
331
309
|
/** Known configuration locations and their parsers. */
|
|
332
310
|
declare const CONFIG_FILES: {
|
|
@@ -376,7 +354,7 @@ interface RequireConfigKeySpec {
|
|
|
376
354
|
/** Only unconditional defaults may reduce severity; omitted means conditional. */
|
|
377
355
|
readonly defaultSafety?: 'unconditional' | 'conditional';
|
|
378
356
|
/**
|
|
379
|
-
* Severity
|
|
357
|
+
* Severity when an unconditional `documentedDefault` satisfies the requirement. Defaults
|
|
380
358
|
* to `'info'` (advisory). Set `'off'` to silence the finding entirely.
|
|
381
359
|
*/
|
|
382
360
|
readonly defaultSatisfiedSeverity?: Severity | 'off';
|
|
@@ -398,6 +376,28 @@ declare const overrideBindings: <Id extends string>(rule: Rule<Id>, overrides: P
|
|
|
398
376
|
/** Build a Rule from a per-PM table of {file, keyPath, value, message}. */
|
|
399
377
|
declare const requireConfigKey: <const Id extends string>(options: RequireConfigKeyOptions<Id>) => Rule<Id>;
|
|
400
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
|
|
401
401
|
//#region src/core/contracts/errors.d.ts
|
|
402
402
|
declare class SiroError extends Error {
|
|
403
403
|
readonly exitCode: number;
|
|
@@ -410,9 +410,6 @@ declare class UsageError extends SiroError {
|
|
|
410
410
|
constructor(message: string);
|
|
411
411
|
}
|
|
412
412
|
//#endregion
|
|
413
|
-
//#region src/version.d.ts
|
|
414
|
-
declare const version: string;
|
|
415
|
-
//#endregion
|
|
416
413
|
//#region src/adapters/node-paths.d.ts
|
|
417
414
|
declare const asAbsPath: (value: string) => AbsPath;
|
|
418
415
|
//#endregion
|
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, 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 };
|
|
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
|