@pho9ubenaa/siro 0.3.0 → 0.4.0
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 +38 -30
- package/README.md +47 -37
- package/dist/cli.mjs +122 -298
- package/dist/index.d.mts +127 -396
- package/dist/index.mjs +3 -4
- package/dist/node-io-BdyNbvzO.mjs +2211 -0
- package/package.json +13 -16
- package/dist/node-io-CUcMwlGH.mjs +0 -2578
package/dist/index.d.mts
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import * as vb from "valibot";
|
|
2
2
|
|
|
3
|
+
//#region src/domain/entities/config-value.d.ts
|
|
4
|
+
declare const CODEC_KINDS: readonly ["json", "npmrc", "toml", "yaml"];
|
|
5
|
+
type CodecKind = (typeof CODEC_KINDS)[number];
|
|
6
|
+
/** Scalar value that can be written back to a config file. */
|
|
7
|
+
type ConfigValue = string | number | boolean;
|
|
8
|
+
/** Parsed keys are unvalidated; each rule narrows the values it reads. */
|
|
9
|
+
interface ParsedConfig {
|
|
10
|
+
readonly [key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
/** A (possibly nested) key path, guaranteed to have at least one segment. */
|
|
13
|
+
type KeyPath = readonly [string, ...string[]];
|
|
14
|
+
/** Parser values may include nested arrays and TOML dates. */
|
|
15
|
+
type ConfigReadValue = unknown;
|
|
16
|
+
/** Read an own key path; absent or non-mapping parents yield undefined. */
|
|
17
|
+
declare const getByPath: (config: ParsedConfig, keyPath: KeyPath) => ConfigReadValue;
|
|
18
|
+
//#endregion
|
|
3
19
|
//#region src/shared/paths.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* Branded path types. Surface the distinction between
|
|
6
|
-
* - `AbsPath`: absolute filesystem paths handed to the FS boundary
|
|
7
|
-
* (`FileSystem.readText` etc.), and
|
|
8
|
-
* - `RelPath`: paths relative to a repo root, accepted by
|
|
9
|
-
* `RepoContext` and resolved via `resolveIn`.
|
|
10
|
-
*
|
|
11
|
-
* The brand is a structural tag with no runtime footprint — the cast in
|
|
12
|
-
* `asAbsPath` / `asRelPath` is the only place either type is minted, so
|
|
13
|
-
* passing a raw `string` to an FS or repo API now requires the caller to
|
|
14
|
-
* declare intent rather than silently mixing the two flavours.
|
|
15
|
-
*/
|
|
16
20
|
declare const AbsPathBrand: unique symbol;
|
|
17
21
|
declare const RelPathBrand: unique symbol;
|
|
18
22
|
type AbsPath = string & {
|
|
@@ -21,10 +25,8 @@ type AbsPath = string & {
|
|
|
21
25
|
type RelPath = string & {
|
|
22
26
|
readonly [RelPathBrand]: true;
|
|
23
27
|
};
|
|
24
|
-
|
|
25
|
-
declare const
|
|
26
|
-
/** Tag a string as repo-root-relative. The caller vouches for the shape. */
|
|
27
|
-
declare const asRelPath: (path: string) => RelPath;
|
|
28
|
+
declare const asAbsPath: (value: string) => AbsPath;
|
|
29
|
+
declare const asRelPath: (value: string) => RelPath;
|
|
28
30
|
//#endregion
|
|
29
31
|
//#region src/domain/ports/file-system.d.ts
|
|
30
32
|
/** IO boundary: swap in memfs (or any other backend) for tests. */
|
|
@@ -33,54 +35,6 @@ interface FileSystem {
|
|
|
33
35
|
exists: (path: AbsPath) => boolean;
|
|
34
36
|
}
|
|
35
37
|
//#endregion
|
|
36
|
-
//#region src/adapters/node-file-system.d.ts
|
|
37
|
-
declare const nodeFileSystem: FileSystem;
|
|
38
|
-
//#endregion
|
|
39
|
-
//#region src/domain/ports/io.d.ts
|
|
40
|
-
/** Output sink for commands; injectable so tests can drive a string buffer. */
|
|
41
|
-
interface IO {
|
|
42
|
-
stdout: (line: string) => void;
|
|
43
|
-
stderr: (line: string) => void;
|
|
44
|
-
}
|
|
45
|
-
//#endregion
|
|
46
|
-
//#region src/adapters/node-io.d.ts
|
|
47
|
-
declare const nodeIO: IO;
|
|
48
|
-
//#endregion
|
|
49
|
-
//#region src/domain/entities/config-value.d.ts
|
|
50
|
-
declare const CODEC_KINDS: readonly ["json", "npmrc", "toml", "yaml"];
|
|
51
|
-
type CodecKind = (typeof CODEC_KINDS)[number];
|
|
52
|
-
/** Scalar value that can be written back to a config file. */
|
|
53
|
-
type ConfigValue = string | number | boolean;
|
|
54
|
-
/** Scalar value as it appears when reading (codecs may produce `null`). */
|
|
55
|
-
type ConfigScalar = ConfigValue | null;
|
|
56
|
-
/** Recursive, structurally typed view of a parsed config file. */
|
|
57
|
-
interface ParsedConfig {
|
|
58
|
-
readonly [key: string]: ConfigScalar | readonly ConfigScalar[] | ParsedConfig;
|
|
59
|
-
}
|
|
60
|
-
/** A (possibly nested) key path, guaranteed to have at least one segment. */
|
|
61
|
-
type KeyPath = readonly [string, ...string[]];
|
|
62
|
-
/** A single key to set. */
|
|
63
|
-
interface KeyAssignment {
|
|
64
|
-
readonly keyPath: KeyPath;
|
|
65
|
-
readonly value: ConfigValue;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Value a rule's `check` saw at the target key, as returned by `getByPath`.
|
|
69
|
-
* Runtime caveat: YAML/TOML parsers can yield host values (e.g. `Date`)
|
|
70
|
-
* nested inside `ParsedConfig` even though the type does not name them —
|
|
71
|
-
* compare timestamps by `valueOf()`, not by type narrowing on this union.
|
|
72
|
-
*/
|
|
73
|
-
type ConfigReadValue = ConfigScalar | readonly ConfigScalar[] | ParsedConfig | undefined;
|
|
74
|
-
/**
|
|
75
|
-
* Look up a nested value by key path; `undefined` if any segment is missing.
|
|
76
|
-
*
|
|
77
|
-
* Part of the public surface (re-exported from `src/index.ts`): embedders
|
|
78
|
-
* writing custom rules receive the same `config: ParsedConfig` shape that
|
|
79
|
-
* built-in rules see, so they need the canonical traversal helper rather
|
|
80
|
-
* than re-implementing `null`-vs-missing semantics per rule.
|
|
81
|
-
*/
|
|
82
|
-
declare const getByPath: (config: ParsedConfig, keyPath: KeyPath) => ConfigScalar | readonly ConfigScalar[] | ParsedConfig | undefined;
|
|
83
|
-
//#endregion
|
|
84
38
|
//#region src/domain/entities/pms.d.ts
|
|
85
39
|
/**
|
|
86
40
|
* Single source of truth for package managers and severities.
|
|
@@ -94,25 +48,25 @@ type Severity = (typeof SEVERITIES)[number];
|
|
|
94
48
|
declare const isPM: (value: string) => value is PM;
|
|
95
49
|
declare const isSeverity: (value: string) => value is Severity;
|
|
96
50
|
//#endregion
|
|
51
|
+
//#region src/domain/entities/project-type.d.ts
|
|
52
|
+
declare const PROJECT_TYPES: readonly ["application", "package"];
|
|
53
|
+
type ProjectType = (typeof PROJECT_TYPES)[number];
|
|
54
|
+
//#endregion
|
|
97
55
|
//#region src/domain/schemas/package-json.d.ts
|
|
98
56
|
declare const PackageJsonSchema: vb.LooseObjectSchema<{
|
|
99
|
-
readonly files: vb.
|
|
100
|
-
readonly name: vb.
|
|
101
|
-
readonly packageManager: vb.
|
|
102
|
-
readonly private: vb.OptionalSchema<vb.
|
|
103
|
-
readonly publishConfig: vb.
|
|
104
|
-
readonly access: vb.
|
|
105
|
-
}, undefined>, undefined
|
|
106
|
-
readonly trustedDependencies: vb.
|
|
57
|
+
readonly files: vb.OptionalSchema<vb.ArraySchema<vb.StringSchema<undefined>, undefined>, undefined>;
|
|
58
|
+
readonly name: vb.OptionalSchema<vb.StringSchema<undefined>, undefined>;
|
|
59
|
+
readonly packageManager: vb.OptionalSchema<vb.StringSchema<undefined>, undefined>;
|
|
60
|
+
readonly private: vb.OptionalSchema<vb.BooleanSchema<undefined>, undefined>;
|
|
61
|
+
readonly publishConfig: vb.OptionalSchema<vb.LooseObjectSchema<{
|
|
62
|
+
readonly access: vb.OptionalSchema<vb.PicklistSchema<["public", "restricted", "private"], undefined>, undefined>;
|
|
63
|
+
}, undefined>, undefined>;
|
|
64
|
+
readonly trustedDependencies: vb.OptionalSchema<vb.ArraySchema<vb.StringSchema<undefined>, undefined>, undefined>;
|
|
107
65
|
}, undefined>;
|
|
108
66
|
type PackageJson = vb.InferOutput<typeof PackageJsonSchema>;
|
|
109
67
|
//#endregion
|
|
110
|
-
//#region src/domain/entities/project-type.d.ts
|
|
111
|
-
declare const PROJECT_TYPES: readonly ["application", "package"];
|
|
112
|
-
type ProjectType = (typeof PROJECT_TYPES)[number];
|
|
113
|
-
//#endregion
|
|
114
68
|
//#region src/domain/ports/repo-context.d.ts
|
|
115
|
-
/** Read-only view of a repository, passed to every rule's `check
|
|
69
|
+
/** Read-only view of a repository, passed to every rule's `check`. */
|
|
116
70
|
interface RepoContext {
|
|
117
71
|
readonly root: AbsPath;
|
|
118
72
|
exists: (relPath: RelPath) => boolean;
|
|
@@ -120,111 +74,58 @@ interface RepoContext {
|
|
|
120
74
|
readonly packageJson: PackageJson | undefined;
|
|
121
75
|
readonly projectType?: ProjectType;
|
|
122
76
|
}
|
|
77
|
+
/** Settings read during one evaluation share its parser and cache. */
|
|
78
|
+
interface RuleContext extends RepoContext {
|
|
79
|
+
readConfig: (file: ConfigFileRef) => ParsedConfig;
|
|
80
|
+
}
|
|
123
81
|
//#endregion
|
|
124
82
|
//#region src/domain/entities/rule.d.ts
|
|
125
|
-
|
|
126
|
-
* What a rule binding points at on disk: a parsed config file or an existence
|
|
127
|
-
* check (e.g. "a lockfile is committed"). `path` is a branded `RelPath` minted
|
|
128
|
-
* once at the ref's definition (CONFIG_FILES), so the readText boundary is
|
|
129
|
-
* type-checked and a stray absolute path can't slip in as a string.
|
|
130
|
-
*/
|
|
131
|
-
type ConfigFileRef = {
|
|
83
|
+
interface ConfigFileRef {
|
|
132
84
|
readonly kind: CodecKind;
|
|
133
85
|
readonly path: RelPath;
|
|
86
|
+
}
|
|
87
|
+
interface SetKeyOperation {
|
|
88
|
+
readonly op: 'setKey';
|
|
89
|
+
readonly file: ConfigFileRef;
|
|
90
|
+
readonly keyPath: KeyPath;
|
|
91
|
+
readonly value: ConfigValue;
|
|
92
|
+
}
|
|
93
|
+
/** A check chooses one remedy for the state it observed. siro does not apply it. */
|
|
94
|
+
type Remediation = {
|
|
95
|
+
readonly kind: 'automatic';
|
|
96
|
+
readonly operations: readonly [SetKeyOperation, ...SetKeyOperation[]];
|
|
97
|
+
readonly steps?: never;
|
|
134
98
|
} | {
|
|
135
|
-
readonly kind: '
|
|
136
|
-
readonly
|
|
99
|
+
readonly kind: 'manual';
|
|
100
|
+
readonly steps: readonly [string, ...string[]];
|
|
101
|
+
readonly operations?: never;
|
|
137
102
|
};
|
|
138
|
-
/** Result of evaluating a single rule binding against a repo. */
|
|
139
103
|
type CheckStatus = {
|
|
140
104
|
readonly state: 'ok';
|
|
105
|
+
} | {
|
|
106
|
+
readonly state: 'na';
|
|
141
107
|
} | {
|
|
142
108
|
readonly state: 'violation';
|
|
143
|
-
readonly message: string;
|
|
109
|
+
readonly message: string; /** Override the primary file when the violation concerns another input. */
|
|
110
|
+
readonly file?: RelPath;
|
|
144
111
|
readonly expected?: ConfigValue;
|
|
145
|
-
readonly actual?: ConfigReadValue;
|
|
146
|
-
/**
|
|
147
|
-
* Dynamic severity for this specific violation, overriding both
|
|
148
|
-
* {@link AutoRuleBinding.severity} and {@link Rule.severity}. Lets a
|
|
149
|
-
* `check` distinguish "unset but PM-default safe" (advisory) from
|
|
150
|
-
* "explicitly weakened" (full severity). A user `rules` override in
|
|
151
|
-
* config still wins (see `applyConfig`).
|
|
152
|
-
*/
|
|
112
|
+
readonly actual?: ConfigReadValue; /** User configuration takes precedence over this per-result severity. */
|
|
153
113
|
readonly severity?: Severity;
|
|
154
|
-
|
|
155
|
-
* Manual remediation steps surfaced verbatim on the finding. Lets a
|
|
156
|
-
* `check` signal "the setKey fix ops cannot resolve this state; the
|
|
157
|
-
* user has to intervene" — e.g. when an explicit user override would
|
|
158
|
-
* have to be deleted first. When non-empty, `Finding.fix` is emitted
|
|
159
|
-
* empty so an external fixer never writes ops the user's existing
|
|
160
|
-
* config would defeat.
|
|
161
|
-
*/
|
|
162
|
-
readonly manualSteps?: readonly string[];
|
|
163
|
-
} | {
|
|
164
|
-
readonly state: 'na';
|
|
165
|
-
};
|
|
166
|
-
/** A remediation step produced by a rule's `fix`. */
|
|
167
|
-
type FixOp = {
|
|
168
|
-
readonly op: 'setKey';
|
|
169
|
-
readonly file: ConfigFileRef;
|
|
170
|
-
readonly keyPath: KeyPath;
|
|
171
|
-
readonly value: ConfigValue;
|
|
172
|
-
} | {
|
|
173
|
-
readonly op: 'ensureFileTracked';
|
|
174
|
-
readonly file: ConfigFileRef;
|
|
175
|
-
readonly message: string;
|
|
176
|
-
} | {
|
|
177
|
-
readonly op: 'note';
|
|
178
|
-
readonly message: string;
|
|
179
|
-
readonly file?: ConfigFileRef;
|
|
114
|
+
readonly remediation?: Remediation;
|
|
180
115
|
};
|
|
181
|
-
/**
|
|
182
|
-
* - `auto`: `fix` produces setKey ops an external fixer can apply
|
|
183
|
-
* mechanically (surfaced via `Finding.fix`; see docs/json-output.md).
|
|
184
|
-
* - `advisory`: `fix` only produces notes/ensureFileTracked hints.
|
|
185
|
-
*/
|
|
186
|
-
type FixKind = 'auto' | 'advisory';
|
|
187
116
|
/** Display-only package-manager version metadata. */
|
|
188
117
|
interface VersionNote {
|
|
189
118
|
readonly configAvailableSince?: string;
|
|
190
119
|
readonly defaultSafeSince?: string;
|
|
191
120
|
readonly note?: string;
|
|
192
121
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}>;
|
|
196
|
-
type AdvisoryOp = Extract<FixOp, {
|
|
197
|
-
op: 'note' | 'ensureFileTracked';
|
|
198
|
-
}>;
|
|
199
|
-
interface AutoRuleBinding {
|
|
200
|
-
readonly file: ConfigFileRef;
|
|
201
|
-
readonly fixKind: 'auto';
|
|
202
|
-
/** Official package-manager doc URL for the setting this binding writes. */
|
|
203
|
-
readonly docs?: string;
|
|
204
|
-
/**
|
|
205
|
-
* Per-binding severity. When set, it shadows {@link Rule.severity} for this
|
|
206
|
-
* PM only — used when a package manager's safe default already mitigates
|
|
207
|
-
* the threat (so the binding is informational) while another PM with the
|
|
208
|
-
* same rule still warrants the rule-wide level. A user config `rules`
|
|
209
|
-
* override always wins over both.
|
|
210
|
-
*/
|
|
211
|
-
readonly severity?: Severity;
|
|
212
|
-
readonly versionNote?: VersionNote;
|
|
213
|
-
check: (ctx: RepoContext, config: ParsedConfig) => CheckStatus;
|
|
214
|
-
fix: (ctx: RepoContext) => readonly SetKeyOp[];
|
|
215
|
-
}
|
|
216
|
-
interface AdvisoryRuleBinding {
|
|
217
|
-
readonly file: ConfigFileRef;
|
|
218
|
-
readonly fixKind: 'advisory';
|
|
219
|
-
/** Official package-manager doc URL for the setting this binding describes. */
|
|
122
|
+
interface RuleBinding {
|
|
123
|
+
readonly file?: ConfigFileRef;
|
|
220
124
|
readonly docs?: string;
|
|
221
|
-
/** See {@link AutoRuleBinding.severity}. */
|
|
222
125
|
readonly severity?: Severity;
|
|
223
126
|
readonly versionNote?: VersionNote;
|
|
224
|
-
check: (ctx:
|
|
225
|
-
fix: (ctx: RepoContext) => readonly AdvisoryOp[];
|
|
127
|
+
check: (ctx: RuleContext, config: ParsedConfig) => CheckStatus;
|
|
226
128
|
}
|
|
227
|
-
type RuleBinding = AutoRuleBinding | AdvisoryRuleBinding;
|
|
228
129
|
/** A package-manager-agnostic security intent, realized per PM via `bindings`. */
|
|
229
130
|
interface Rule<Id extends string = string> {
|
|
230
131
|
readonly id: Id;
|
|
@@ -237,6 +138,18 @@ interface Rule<Id extends string = string> {
|
|
|
237
138
|
/** Absence of a PM key means the rule does not apply (N/A) to that PM. */
|
|
238
139
|
readonly bindings: Partial<Record<PM, RuleBinding>>;
|
|
239
140
|
}
|
|
141
|
+
declare const defineRule: <const Id extends string>(rule: Rule<Id>) => Rule<Id>;
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/domain/builtin-rules.d.ts
|
|
144
|
+
declare const rules: readonly [Rule<"advisory-check">, Rule<"approved-git-repos">, Rule<"audit-suppression">, Rule<"block-auto-install">, Rule<"block-exotic-subdeps">, Rule<"bun-security-scanner">, Rule<"checksum-verification">, Rule<"commit-lockfile">, Rule<"dependency-overrides">, Rule<"disable-lifecycle-scripts">, Rule<"enforce-strict-ssl">, Rule<"files-field">, Rule<"frozen-lockfile">, Rule<"frozen-store">, Rule<"hardened-mode">, Rule<"minimum-release-age">, Rule<"named-registries">, Rule<"paranoid-mode">, Rule<"patched-dependencies">, Rule<"pin-exact-versions">, Rule<"provenance">, Rule<"publish-access">, Rule<"store-server">, Rule<"strict-allow-scripts">, Rule<"strict-release-age">, Rule<"strict-store-integrity">, Rule<"trust-policy">];
|
|
145
|
+
type BuiltinRuleId = (typeof rules)[number]['id'];
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/domain/ports/io.d.ts
|
|
148
|
+
/** Output sink for commands; injectable so tests can drive a string buffer. */
|
|
149
|
+
interface IO {
|
|
150
|
+
stdout: (line: string) => void;
|
|
151
|
+
stderr: (line: string) => void;
|
|
152
|
+
}
|
|
240
153
|
//#endregion
|
|
241
154
|
//#region src/domain/entities/lint-result.d.ts
|
|
242
155
|
interface Finding {
|
|
@@ -245,22 +158,7 @@ interface Finding {
|
|
|
245
158
|
readonly severity: Severity;
|
|
246
159
|
readonly message: string;
|
|
247
160
|
readonly file?: string;
|
|
248
|
-
readonly
|
|
249
|
-
/**
|
|
250
|
-
* Machine-readable remediation: the binding's fix ops, verbatim. Empty
|
|
251
|
-
* when `manualSteps` is present (writing the ops would be defeated by the
|
|
252
|
-
* user state that produced the steps). Advisory bindings contribute their
|
|
253
|
-
* `note` / `ensureFileTracked` ops here so external fixers can surface
|
|
254
|
-
* them. Part of the json output contract — see docs/json-output.md.
|
|
255
|
-
*/
|
|
256
|
-
readonly fix: readonly FixOp[];
|
|
257
|
-
/** Manual remediation steps, verbatim from the check (see CheckStatus). */
|
|
258
|
-
readonly manualSteps?: readonly string[];
|
|
259
|
-
/**
|
|
260
|
-
* Pinned to {@link ConfigValue} (no `null`) so it stays in lockstep with
|
|
261
|
-
* `CheckStatus.violation.expected`. Widening to ConfigScalar would have
|
|
262
|
-
* advertised a `null` outcome that no current rule can produce.
|
|
263
|
-
*/
|
|
161
|
+
readonly remediation?: Remediation;
|
|
264
162
|
readonly expected?: ConfigValue;
|
|
265
163
|
readonly actual?: ConfigReadValue;
|
|
266
164
|
/** Official PM doc anchor for the setting this finding is about, if any. */
|
|
@@ -272,32 +170,54 @@ interface LintResult {
|
|
|
272
170
|
}
|
|
273
171
|
//#endregion
|
|
274
172
|
//#region src/domain/ports/reporter.d.ts
|
|
275
|
-
/**
|
|
276
|
-
* Renderer from `LintResult` to user-facing output.
|
|
277
|
-
*
|
|
278
|
-
* Reporters never touch the filesystem and never spawn processes — every
|
|
279
|
-
* output byte flows through the injected `IO` port. They MAY however
|
|
280
|
-
* consult `process.env` for environmental shape (TTY colour support,
|
|
281
|
-
* NO_COLOR / FORCE_COLOR, CI detection) because that information has no
|
|
282
|
-
* `IO`-port equivalent and treating it as I/O would force the application
|
|
283
|
-
* layer to thread a snapshot through every call site for a value the
|
|
284
|
-
* adapter can read in a single line.
|
|
285
|
-
*
|
|
286
|
-
* Implementations live in the adapter layer (src/adapters/reporters/),
|
|
287
|
-
* so the env access stays out of the domain.
|
|
288
|
-
*/
|
|
289
173
|
interface Reporter<Name extends string = string> {
|
|
290
174
|
readonly name: Name;
|
|
291
175
|
format: (result: LintResult, io: IO) => void;
|
|
292
176
|
}
|
|
177
|
+
//#endregion
|
|
178
|
+
//#region src/domain/entities/siro-config.d.ts
|
|
179
|
+
/** Per-rule setting. `'off'` disables; a Severity overrides the default level. */
|
|
180
|
+
type RuleSetting = Severity | 'off';
|
|
293
181
|
/**
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
182
|
+
* User-facing config returned by `defineConfig` in `siro.config.{ts,mjs,js}`.
|
|
183
|
+
*
|
|
184
|
+
* defineConfig({
|
|
185
|
+
* pms: ['npm', 'pnpm'], // restrict detection
|
|
186
|
+
* rules: { provenance: 'off' }, // disable / override severity
|
|
187
|
+
* customRules: [myRule], // extend
|
|
188
|
+
* reporters: [mySarifReporter], // extend
|
|
189
|
+
* })
|
|
299
190
|
*/
|
|
300
|
-
|
|
191
|
+
interface SiroConfig {
|
|
192
|
+
readonly pms?: readonly PM[];
|
|
193
|
+
readonly projectType?: ProjectType;
|
|
194
|
+
readonly rules?: Readonly<Partial<Record<BuiltinRuleId | (string & Record<never, never>), RuleSetting>>>;
|
|
195
|
+
readonly customRules?: readonly Rule[];
|
|
196
|
+
readonly reporters?: readonly Reporter[];
|
|
197
|
+
}
|
|
198
|
+
/** Identity helper for type-checked config files. */
|
|
199
|
+
declare const defineConfig: (config: SiroConfig) => SiroConfig;
|
|
200
|
+
//#endregion
|
|
201
|
+
//#region src/application/lint.d.ts
|
|
202
|
+
interface LintOptions {
|
|
203
|
+
readonly cwd: AbsPath;
|
|
204
|
+
readonly fs?: FileSystem;
|
|
205
|
+
readonly pm?: PM;
|
|
206
|
+
readonly projectType?: ProjectType;
|
|
207
|
+
/** Explicit configuration; the library never imports files from the target repository. */
|
|
208
|
+
readonly config?: SiroConfig;
|
|
209
|
+
}
|
|
210
|
+
declare const lint: (options: LintOptions) => LintResult;
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/adapters/config-loader.d.ts
|
|
213
|
+
/** Load the first matching config; executable imports always use the real filesystem. */
|
|
214
|
+
declare const loadConfig: (cwd: AbsPath, nodeVersion?: string) => Promise<SiroConfig | undefined>;
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/adapters/node-file-system.d.ts
|
|
217
|
+
declare const nodeFileSystem: FileSystem;
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region src/adapters/node-io.d.ts
|
|
220
|
+
declare const nodeIO: IO;
|
|
301
221
|
//#endregion
|
|
302
222
|
//#region src/adapters/reporters/github.d.ts
|
|
303
223
|
/** Emit GitHub Actions workflow commands (annotations on PRs). */
|
|
@@ -315,62 +235,17 @@ declare const BUILTINS: readonly [Reporter<"pretty">, Reporter<"json">, Reporter
|
|
|
315
235
|
declare const BUILTIN_REPORTER_NAMES: readonly BuiltinReporterName[];
|
|
316
236
|
/** Literal union of every built-in reporter name. */
|
|
317
237
|
type BuiltinReporterName = (typeof BUILTINS)[number]['name'];
|
|
318
|
-
/** Reporter lookup table — pass an explicit value to keep calls hermetic. */
|
|
319
|
-
interface ReporterRegistry {
|
|
320
|
-
get: (name: string) => Reporter | undefined;
|
|
321
|
-
list: () => readonly string[];
|
|
322
|
-
}
|
|
323
|
-
/** Build a registry from the builtins plus any extras (later wins on collision). */
|
|
324
|
-
declare const createRegistry: (extras?: readonly Reporter[]) => ReporterRegistry;
|
|
325
238
|
//#endregion
|
|
326
239
|
//#region src/application/commands/lint.d.ts
|
|
327
|
-
interface LintOptions {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
* path before branding it; siro's CLI does this in `cli.ts`. Keeping the
|
|
331
|
-
* brand here lets the application layer stay free of `node:path`.
|
|
332
|
-
*/
|
|
333
|
-
cwd: AbsPath;
|
|
334
|
-
/** Restrict to a single PM; otherwise PMs are auto-detected. */
|
|
335
|
-
pm?: PM;
|
|
336
|
-
/** Select application or published-package policy; otherwise infer it. */
|
|
337
|
-
projectType?: ProjectType;
|
|
338
|
-
/** Reporter name or instance; defaults to `pretty`. */
|
|
339
|
-
reporter?: string | Reporter;
|
|
340
|
-
/** Extra reporters to make available by name (merged with the user config). */
|
|
341
|
-
reporters?: readonly Reporter[];
|
|
342
|
-
/**
|
|
343
|
-
* Programmatic custom rules to evaluate alongside the builtins and any
|
|
344
|
-
* `customRules` from the user config. Mirrors `SiroConfig.customRules`
|
|
345
|
-
* but is supplied at the call site, so embedders can compose rulesets
|
|
346
|
-
* without writing a config file. Ids must be unique across builtins,
|
|
347
|
-
* config-supplied custom rules, and this list — collisions throw a
|
|
348
|
-
* `ConfigError` (exit 2), matching how `loadConfig` rejects them.
|
|
349
|
-
*/
|
|
350
|
-
customRules?: readonly Rule[];
|
|
351
|
-
/** Show (and fail on) findings at or above this severity. */
|
|
352
|
-
severity?: Severity;
|
|
353
|
-
/**
|
|
354
|
-
* Inject a non-default FS (e.g. memfs in tests). Caveat: `siro.config.{ts,mjs,js}`
|
|
355
|
-
* is imported from the REAL disk — only the config's existence is probed
|
|
356
|
-
* through this FS. A config that lives solely in an injected FS won't load.
|
|
357
|
-
*/
|
|
358
|
-
fs?: FileSystem;
|
|
240
|
+
interface LintCommandOptions extends LintOptions {
|
|
241
|
+
readonly reporter?: string | Reporter;
|
|
242
|
+
readonly severity?: Severity;
|
|
359
243
|
}
|
|
360
|
-
/**
|
|
361
|
-
declare const lintCommand: (options:
|
|
244
|
+
/** Evaluate and report. Executable config loading belongs to the CLI adapter. */
|
|
245
|
+
declare const lintCommand: (options: LintCommandOptions, io: IO) => Promise<number>;
|
|
362
246
|
//#endregion
|
|
363
247
|
//#region src/domain/entities/config-files.d.ts
|
|
364
|
-
/**
|
|
365
|
-
* Canonical {@link ConfigFileRef}s for every package-manager config file siro
|
|
366
|
-
* knows how to read. Centralizing them keeps each rule module from hardcoding
|
|
367
|
-
* `kind` / `path` pairs, so renaming a file (or fixing a typo in its path) is a
|
|
368
|
-
* single-line change. This is also the one place `RelPath` is minted for a
|
|
369
|
-
* config ref — every downstream binding inherits the brand, so no rule needs
|
|
370
|
-
* an ad-hoc `asRelPath(ref.path)` cast at the FS boundary.
|
|
371
|
-
*
|
|
372
|
-
* Add a new entry here when introducing support for a new package manager.
|
|
373
|
-
*/
|
|
248
|
+
/** Known configuration locations and their parsers. */
|
|
374
249
|
declare const CONFIG_FILES: {
|
|
375
250
|
readonly aubeWorkspace: {
|
|
376
251
|
readonly kind: "yaml";
|
|
@@ -402,119 +277,19 @@ declare const CONFIG_FILES: {
|
|
|
402
277
|
};
|
|
403
278
|
};
|
|
404
279
|
//#endregion
|
|
405
|
-
//#region src/domain/entities/signals.d.ts
|
|
406
|
-
/**
|
|
407
|
-
* Filenames that identify a package manager. `lockfiles[0]` is the
|
|
408
|
-
* canonical/preferred one (used by lint messages and error
|
|
409
|
-
* messages); the rest are legacy/alternative forms the PM writes itself.
|
|
410
|
-
*
|
|
411
|
-
* `lockfiles` and `configs` are **detection evidence** — their presence means
|
|
412
|
-
* the PM is in use. `reusesLockfiles` is NOT evidence: it lists other PMs'
|
|
413
|
-
* lockfile shapes that this PM will reuse rather than writing its own (aube),
|
|
414
|
-
* so `commit-lockfile` accepts them but `detectPMs` ignores them — otherwise a
|
|
415
|
-
* plain pnpm/npm repo would false-positive as aube. Keeping the two lists
|
|
416
|
-
* separate is what lets detection stay a simple "any signal present?" check.
|
|
417
|
-
*/
|
|
418
|
-
interface PMSignals {
|
|
419
|
-
readonly lockfiles: readonly [string, ...string[]];
|
|
420
|
-
readonly configs: readonly string[];
|
|
421
|
-
/** Other PMs' lockfiles this PM reuses; satisfies commit-lockfile, not detection. */
|
|
422
|
-
readonly reusesLockfiles?: readonly string[];
|
|
423
|
-
}
|
|
424
|
-
declare const PM_SIGNALS: {
|
|
425
|
-
readonly aube: {
|
|
426
|
-
readonly configs: readonly [RelPath];
|
|
427
|
-
readonly lockfiles: readonly ["aube-lock.yaml"];
|
|
428
|
-
readonly reusesLockfiles: readonly ["pnpm-lock.yaml", "package-lock.json", "npm-shrinkwrap.json", "yarn.lock", "bun.lock", "bun.lockb", "deno.lock"];
|
|
429
|
-
};
|
|
430
|
-
readonly bun: {
|
|
431
|
-
readonly configs: readonly [RelPath];
|
|
432
|
-
readonly lockfiles: readonly ["bun.lock", "bun.lockb"];
|
|
433
|
-
};
|
|
434
|
-
readonly deno: {
|
|
435
|
-
readonly configs: readonly [RelPath];
|
|
436
|
-
readonly lockfiles: readonly ["deno.lock"];
|
|
437
|
-
};
|
|
438
|
-
readonly npm: {
|
|
439
|
-
readonly configs: readonly [];
|
|
440
|
-
readonly lockfiles: readonly ["package-lock.json", "npm-shrinkwrap.json"];
|
|
441
|
-
};
|
|
442
|
-
readonly pnpm: {
|
|
443
|
-
readonly configs: readonly [RelPath];
|
|
444
|
-
readonly lockfiles: readonly ["pnpm-lock.yaml"];
|
|
445
|
-
};
|
|
446
|
-
readonly yarn: {
|
|
447
|
-
readonly configs: readonly [RelPath];
|
|
448
|
-
readonly lockfiles: readonly ["yarn.lock"];
|
|
449
|
-
};
|
|
450
|
-
};
|
|
451
|
-
//#endregion
|
|
452
|
-
//#region src/domain/builtin-rules.d.ts
|
|
453
|
-
declare const rules: readonly [Rule<"advisory-check">, Rule<"approved-git-repos">, Rule<"audit-suppression">, Rule<"block-auto-install">, Rule<"block-exotic-subdeps">, Rule<"bun-security-scanner">, Rule<"checksum-verification">, Rule<"commit-lockfile">, Rule<"dependency-overrides">, Rule<"disable-lifecycle-scripts">, Rule<"enforce-strict-ssl">, Rule<"files-field">, Rule<"frozen-lockfile">, Rule<"frozen-store">, Rule<"hardened-mode">, Rule<"minimum-release-age">, Rule<"named-registries">, Rule<"paranoid-mode">, Rule<"patched-dependencies">, Rule<"pin-exact-versions">, Rule<"provenance">, Rule<"publish-access">, Rule<"store-server">, Rule<"strict-allow-scripts">, Rule<"strict-release-age">, Rule<"strict-store-integrity">, Rule<"trust-policy">];
|
|
454
|
-
type BuiltinRuleId = (typeof rules)[number]['id'];
|
|
455
|
-
//#endregion
|
|
456
|
-
//#region src/domain/entities/siro-config.d.ts
|
|
457
|
-
/** Per-rule setting. `'off'` disables; a Severity overrides the default level. */
|
|
458
|
-
type RuleSetting = Severity | 'off';
|
|
459
|
-
/**
|
|
460
|
-
* User-facing config returned by `defineConfig` in `siro.config.{ts,mjs,js}`.
|
|
461
|
-
*
|
|
462
|
-
* defineConfig({
|
|
463
|
-
* pms: ['npm', 'pnpm'], // restrict detection
|
|
464
|
-
* rules: { provenance: 'off' }, // disable / override severity
|
|
465
|
-
* customRules: [myRule], // extend
|
|
466
|
-
* reporters: [mySarifReporter], // extend
|
|
467
|
-
* })
|
|
468
|
-
*/
|
|
469
|
-
interface SiroConfig {
|
|
470
|
-
readonly pms?: readonly PM[];
|
|
471
|
-
readonly projectType?: ProjectType;
|
|
472
|
-
readonly rules?: Readonly<Partial<Record<BuiltinRuleId | (string & Record<never, never>), RuleSetting>>>;
|
|
473
|
-
readonly customRules?: readonly Rule[];
|
|
474
|
-
readonly reporters?: readonly Reporter[];
|
|
475
|
-
}
|
|
476
|
-
/** Identity helper for type-checked config files. */
|
|
477
|
-
declare const defineConfig: (config: SiroConfig) => SiroConfig;
|
|
478
|
-
//#endregion
|
|
479
|
-
//#region src/domain/ports/config-codec.d.ts
|
|
480
|
-
/** Reads a single config-file format into siro's structural view. */
|
|
481
|
-
interface ConfigCodec {
|
|
482
|
-
parse: (text: string) => ParsedConfig;
|
|
483
|
-
}
|
|
484
|
-
/** Resolve the codec for a given file kind. Total over `CodecKind`. */
|
|
485
|
-
type CodecFor = (kind: CodecKind) => ConfigCodec;
|
|
486
|
-
//#endregion
|
|
487
280
|
//#region src/domain/rules/builders/require-config-key.d.ts
|
|
488
281
|
interface RequireConfigKeySpec {
|
|
489
282
|
readonly file: ConfigFileRef;
|
|
490
283
|
readonly keyPath: KeyPath;
|
|
491
|
-
/**
|
|
284
|
+
/** Expected value and proposed replacement; `accept` may allow other values. */
|
|
492
285
|
readonly value: ConfigValue;
|
|
493
286
|
readonly message: string;
|
|
494
287
|
readonly docs?: string;
|
|
495
288
|
readonly severity?: Severity;
|
|
496
289
|
accept?: (actual: unknown) => boolean;
|
|
497
290
|
/**
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
* is implicitly `spec.file` — extras can't redirect to a different file
|
|
501
|
-
* because `check` would then never validate them, leaving the fix ops
|
|
502
|
-
* and `lint` permanently out of step. A rule that legitimately needs to write
|
|
503
|
-
* across multiple files belongs in a hand-written binding (see
|
|
504
|
-
* `disable-lifecycle-scripts` → `overrideBindings`).
|
|
505
|
-
*/
|
|
506
|
-
readonly extraFix?: readonly KeyAssignment[];
|
|
507
|
-
/**
|
|
508
|
-
* PM-documented default for this key. When the user has not set the key
|
|
509
|
-
* AND this default would satisfy `accept`/`value`, the finding is
|
|
510
|
-
* downgraded to {@link defaultSatisfiedSeverity} (default `'info'`) — the
|
|
511
|
-
* threat is mitigated by the PM but explicit pinning is still recommended.
|
|
512
|
-
* - unset: no PM-default protection (legacy behaviour).
|
|
513
|
-
*
|
|
514
|
-
* A CONDITIONAL default (e.g. CI-only: pnpm `frozenLockfile`, aube
|
|
515
|
-
* `preferFrozenLockfile`) may use this field, but the binding's `message`
|
|
516
|
-
* MUST name the condition — the downgrade then reads "covered where it
|
|
517
|
-
* matters most", not "covered unconditionally".
|
|
291
|
+
* An omitted value that is safe across every supported version and target
|
|
292
|
+
* environment emits info. Version-dependent defaults retain full severity.
|
|
518
293
|
*/
|
|
519
294
|
readonly documentedDefault?: ConfigValue;
|
|
520
295
|
/**
|
|
@@ -536,68 +311,24 @@ interface RequireConfigKeyOptions<Id extends string = string> {
|
|
|
536
311
|
/** Return false to short-circuit `check` as N/A (e.g. private packages). */
|
|
537
312
|
applies?: (ctx: RepoContext) => boolean;
|
|
538
313
|
}
|
|
539
|
-
/**
|
|
540
|
-
* Replace one or more bindings on an existing rule, returning a fresh Rule
|
|
541
|
-
* object. Used by rules whose shape outgrows {@link requireConfigKey} for a
|
|
542
|
-
* subset of PMs — they build the simple slots via the builder, then splice
|
|
543
|
-
* the hand-written bindings in via this helper rather than rebuilding from
|
|
544
|
-
* scratch or mutating the source rule.
|
|
545
|
-
*/
|
|
546
314
|
declare const overrideBindings: <Id extends string>(rule: Rule<Id>, overrides: Partial<Rule["bindings"]>) => Rule<Id>;
|
|
547
315
|
/** Build a Rule from a per-PM table of {file, keyPath, value, message}. */
|
|
548
316
|
declare const requireConfigKey: <const Id extends string>(options: RequireConfigKeyOptions<Id>) => Rule<Id>;
|
|
549
317
|
//#endregion
|
|
550
|
-
//#region src/
|
|
551
|
-
declare const renderVersionNoteMessage: (message: string, versionNote: VersionNote | undefined) => string;
|
|
552
|
-
//#endregion
|
|
553
|
-
//#region src/domain/services/detect-pms.d.ts
|
|
554
|
-
declare const detectPMs: (ctx: RepoContext) => PM[];
|
|
555
|
-
//#endregion
|
|
556
|
-
//#region src/domain/services/filter.d.ts
|
|
557
|
-
/** Keep only findings at or above `threshold`, recomputing the summary. */
|
|
558
|
-
declare const filterBySeverity: (result: LintResult, threshold: Severity) => LintResult;
|
|
559
|
-
/**
|
|
560
|
-
* Exit code for a lint run. Non-zero when any finding meets `threshold`
|
|
561
|
-
* (default: only `error` fails the run).
|
|
562
|
-
*/
|
|
563
|
-
declare const exitCodeForLint: (result: LintResult, threshold?: Severity) => number;
|
|
564
|
-
//#endregion
|
|
565
|
-
//#region src/shared/siro-error.d.ts
|
|
566
|
-
/**
|
|
567
|
-
* Structured errors used across siro. The CLI maps them to exit codes in one
|
|
568
|
-
* place so individual commands never have to think about exit semantics.
|
|
569
|
-
* The exit-code table lives in docs/configuration.md S"Exit codes" and
|
|
570
|
-
* src/cli/help.ts (HELP_LINT) -- not restated here.
|
|
571
|
-
*/
|
|
318
|
+
//#region src/shared/errors.d.ts
|
|
572
319
|
declare class SiroError extends Error {
|
|
573
|
-
/** Recommended process exit code for this error. */
|
|
574
320
|
readonly exitCode: number;
|
|
575
321
|
constructor(message: string, exitCode: number);
|
|
576
322
|
}
|
|
577
|
-
//#endregion
|
|
578
|
-
//#region src/shared/config-error.d.ts
|
|
579
323
|
declare class ConfigError extends SiroError {
|
|
580
324
|
constructor(message: string);
|
|
581
325
|
}
|
|
582
|
-
//#endregion
|
|
583
|
-
//#region src/shared/usage-error.d.ts
|
|
584
326
|
declare class UsageError extends SiroError {
|
|
585
327
|
constructor(message: string);
|
|
586
328
|
}
|
|
587
329
|
//#endregion
|
|
588
|
-
//#region src/shared/errors.d.ts
|
|
589
|
-
/**
|
|
590
|
-
* Run `fn` and wrap any non-`ConfigError` failure as a `ConfigError` prefixed
|
|
591
|
-
* with `filePath`. Codec `parse` calls hit raw libraries that
|
|
592
|
-
* throw bare `Error`s; without this wrap the CLI would surface those as
|
|
593
|
-
* exit-1 internal errors and break CI that branches on exit codes. A
|
|
594
|
-
* `ConfigError` that bubbles up from a nested call is re-thrown unchanged so
|
|
595
|
-
* the original `path: message` framing is preserved.
|
|
596
|
-
*/
|
|
597
|
-
declare const wrapCodecError: <TResult>(filePath: string, fn: () => TResult) => TResult;
|
|
598
|
-
//#endregion
|
|
599
330
|
//#region src/version.d.ts
|
|
600
|
-
declare const version
|
|
331
|
+
declare const version: string;
|
|
601
332
|
//#endregion
|
|
602
|
-
export { type AbsPath,
|
|
333
|
+
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 KeyPath, type LintCommandOptions, type LintOptions, type LintResult, type PM, PMS, PROJECT_TYPES, type ParsedConfig, type ProjectType, type RelPath, type Remediation, type RepoContext, type Reporter, type RequireConfigKeyOptions, type Rule, type RuleBinding, type RuleContext, type RuleSetting, SEVERITIES, type SetKeyOperation, type Severity, type SiroConfig, SiroError, UsageError, type VersionNote, asAbsPath, asRelPath, defineConfig, defineRule, getByPath, githubReporter, isPM, isSeverity, jsonReporter, lint, lintCommand, loadConfig, nodeFileSystem, nodeIO, overrideBindings, prettyReporter, requireConfigKey, version };
|
|
603
334
|
//# sourceMappingURL=index.d.mts.map
|