@noctcore/lint-meta-rules 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -60
- package/dist/{chunk-Z7TXSZR4.js → chunk-OYFQKSJN.js} +9 -1
- package/dist/i18n.js +1 -1
- package/dist/index.js +1 -1
- package/dist/prisma.js +3 -3
- package/dist/session.cjs +297 -0
- package/dist/session.d.cts +226 -0
- package/dist/session.d.ts +226 -0
- package/dist/session.js +244 -0
- package/dist/trpc.cjs +122 -0
- package/dist/trpc.d.cts +59 -0
- package/dist/trpc.d.ts +59 -0
- package/dist/trpc.js +73 -0
- package/docs/rules/agents-doc-presence.md +13 -4
- package/docs/rules/canonical-helpers-single-home.md +13 -3
- package/docs/rules/dockerfile-base-image-digest-pin.md +17 -4
- package/docs/rules/eslint-config-no-warn.md +22 -7
- package/docs/rules/file-size-ratchet.md +19 -9
- package/docs/rules/github-actions-least-privilege-permissions.md +20 -11
- package/docs/rules/github-actions-no-template-injection.md +21 -12
- package/docs/rules/github-actions-runner-pinned.md +19 -6
- package/docs/rules/github-actions-sha-pinned.md +19 -5
- package/docs/rules/idempotency-key-parity.md +91 -0
- package/docs/rules/layer-rank.md +17 -6
- package/docs/rules/no-cloned-component-folders.md +13 -4
- package/docs/rules/no-warn-severity.md +14 -3
- package/docs/rules/package-shape.md +13 -6
- package/docs/rules/prisma-method-surface.md +19 -4
- package/docs/rules/security-scanner-version-parity.md +16 -7
- package/docs/rules/service-image-digest-pin.md +21 -7
- package/docs/rules/session-epoch-captured.md +89 -0
- package/docs/rules/session-kind-stamped.md +89 -0
- package/docs/rules/session-landing-declared.md +95 -0
- package/docs/rules/session-mint-callers.md +85 -0
- package/docs/rules/tenant-model-registry-parity.md +18 -4
- package/docs/rules/test-runner-segregation.md +15 -5
- package/docs/rules/test-sibling-enforcement.md +12 -5
- package/docs/rules/test-workspace-enrollment.md +14 -6
- package/docs/rules/translation-dead-keys.md +33 -20
- package/docs/rules/ui-primitive-shape.md +13 -4
- package/docs/rules/workspace-graph-parity.md +15 -5
- package/package.json +18 -8
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { IMetaRule } from '@noctcore/harness';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The source scope every session rule shares: which files are read, and which
|
|
5
|
+
* of them are left alone because they drive the seam directly (tests).
|
|
6
|
+
*/
|
|
7
|
+
interface SessionSourceScopeOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Globs of the application source that can open a session, relative to the
|
|
10
|
+
* repo root. Empty (the default) makes the rule inert: it reads nothing and
|
|
11
|
+
* reports nothing. Point it at shipped code only; a lint-meta rule module that
|
|
12
|
+
* quotes the call in its own pattern is not a call site.
|
|
13
|
+
*/
|
|
14
|
+
readonly sourceGlobs?: readonly string[];
|
|
15
|
+
/** Paths with any of these segments are skipped. Default `node_modules`, `.git`, `dist`, `.turbo`, `coverage`. */
|
|
16
|
+
readonly skipDirs?: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Files ending in any of these are skipped: tests drive the seam directly,
|
|
19
|
+
* including shapes production never produces. Default
|
|
20
|
+
* `['.spec.ts', '.spec.tsx', '.test.ts', '.test.tsx']`.
|
|
21
|
+
*/
|
|
22
|
+
readonly excludeSuffixes?: readonly string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The argument text of each `.<method>(...)` call in `source`.
|
|
26
|
+
*
|
|
27
|
+
* Scanned by balancing parentheses rather than matched with a regex: an options
|
|
28
|
+
* object spans lines and holds its own braces and parens, and a non-greedy
|
|
29
|
+
* regex either stops early or runs on into the next call. A scanner that never
|
|
30
|
+
* finds the closing paren returns what it has, which is the fail-closed
|
|
31
|
+
* direction (more text to search, never less). `.<method>Something(` is not a
|
|
32
|
+
* call of `method`: only whitespace may sit between the name and the paren.
|
|
33
|
+
*/
|
|
34
|
+
declare function callArguments(source: string, method: string): string[];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Options for {@link createSessionEpochCapturedRule}.
|
|
38
|
+
*
|
|
39
|
+
* Ported from Settly, which hardcoded the seam (`beginOrEstablish`), the field
|
|
40
|
+
* (`epoch`) and the seam's own file. With no `call` the rule is inert.
|
|
41
|
+
*/
|
|
42
|
+
interface SessionEpochCapturedOptions extends SessionSourceScopeOptions {
|
|
43
|
+
/** Rule id, for running more than one instance. Default `session-epoch-captured`. */
|
|
44
|
+
readonly id?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The post-credential seam every sign-in passes through on its way to a
|
|
47
|
+
* session, matched as `.<call>(`. Unset or empty = inert.
|
|
48
|
+
*/
|
|
49
|
+
readonly call?: string;
|
|
50
|
+
/** The argument every call must mention: the epoch captured before the credential was read. Default `epoch`. */
|
|
51
|
+
readonly field?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Repo-relative files skipped outright: the seam's own home, which defines
|
|
54
|
+
* the method and may call itself without a captured value. Default: none.
|
|
55
|
+
*/
|
|
56
|
+
readonly exempt?: readonly string[];
|
|
57
|
+
/** How the epoch is captured (e.g. `sessions.readEpoch(userId)`), quoted in the message. Optional. */
|
|
58
|
+
readonly captureCall?: string;
|
|
59
|
+
/** Appended to every message: a pointer to the project's own docs. */
|
|
60
|
+
readonly hint?: string;
|
|
61
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
62
|
+
readonly ciCritical?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Every call into the sign-in seam passes the session epoch it captured before
|
|
66
|
+
* reading the credential.
|
|
67
|
+
*
|
|
68
|
+
* A revoke-all bumps a per-user epoch and a mint refuses to write a session
|
|
69
|
+
* under a stale one. Captured inside the mint, the fence covers the store write
|
|
70
|
+
* and nothing else: the whole credential check (a password hash verify, an
|
|
71
|
+
* OAuth token exchange) is a window in which a sign-in that already read the
|
|
72
|
+
* revoked state still mints a surviving session. So each entry point captures
|
|
73
|
+
* the epoch first and hands it to the seam, and this rule fails any call whose
|
|
74
|
+
* arguments never mention it.
|
|
75
|
+
*/
|
|
76
|
+
declare function createSessionEpochCapturedRule(options?: SessionEpochCapturedOptions): IMetaRule;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Options for {@link createSessionKindStampedRule}.
|
|
80
|
+
*
|
|
81
|
+
* Ported from Settly, which hardcoded the mint (`establishSession`), the field
|
|
82
|
+
* (`kind`) and the one staff-only exemption. With no `mintCall` the rule is inert.
|
|
83
|
+
*/
|
|
84
|
+
interface SessionKindStampedOptions extends SessionSourceScopeOptions {
|
|
85
|
+
/** Rule id, for running more than one instance. Default `session-kind-stamped`. */
|
|
86
|
+
readonly id?: string;
|
|
87
|
+
/** The method whose call mints a session, matched as `.<mintCall>(`. Unset or empty = inert. */
|
|
88
|
+
readonly mintCall?: string;
|
|
89
|
+
/** The session field every mint must stamp. Default `kind`. */
|
|
90
|
+
readonly field?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Repo-relative files whose mints may stamp nothing because they provably can
|
|
93
|
+
* never mint for a principal that needs the field (a self-signup that only
|
|
94
|
+
* ever creates the default kind). Keep it short and write the proof next to
|
|
95
|
+
* each entry. Default: none.
|
|
96
|
+
*/
|
|
97
|
+
readonly allowUnstamped?: readonly string[];
|
|
98
|
+
/** An example of the stamp, quoted in the message (e.g. a conditional spread). Optional. */
|
|
99
|
+
readonly stampExample?: string;
|
|
100
|
+
/** Appended to every message: a pointer to the project's own docs. */
|
|
101
|
+
readonly hint?: string;
|
|
102
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
103
|
+
readonly ciCritical?: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Every session mint stamps the principal's kind onto the session it writes.
|
|
107
|
+
*
|
|
108
|
+
* When the request pipeline reads the kind from the SESSION rather than the
|
|
109
|
+
* database, a session minted without it reads as whatever the reader defaults
|
|
110
|
+
* to. For a fence between two kinds of account that is a silent promotion of
|
|
111
|
+
* one into the other, and every gate stays green because nothing looks.
|
|
112
|
+
*
|
|
113
|
+
* The check, in order of precision:
|
|
114
|
+
*
|
|
115
|
+
* 1. A call whose arguments contain an object LITERAL must mention the field in
|
|
116
|
+
* that literal. Each literal is checked on its own, so one stamped mint does
|
|
117
|
+
* not excuse an unstamped one in the same file.
|
|
118
|
+
* 2. A call with no literal (options built elsewhere) falls back to the file:
|
|
119
|
+
* the file must mention the field somewhere. Coarser, but no silent hole.
|
|
120
|
+
* 3. Otherwise the file must be on `allowUnstamped`.
|
|
121
|
+
*
|
|
122
|
+
* Residual gap, stated rather than hidden: a file that stamps the field on one
|
|
123
|
+
* delegated mint and forgets it on a second delegated mint passes clause 2.
|
|
124
|
+
*/
|
|
125
|
+
declare function createSessionKindStampedRule(options?: SessionKindStampedOptions): IMetaRule;
|
|
126
|
+
|
|
127
|
+
/** One file that opens a door into a session, and where it leaves the caller. */
|
|
128
|
+
interface SessionDoor {
|
|
129
|
+
/** Repo-relative path of the file. */
|
|
130
|
+
readonly file: string;
|
|
131
|
+
/** One of the keys of `landings`. */
|
|
132
|
+
readonly landing: string;
|
|
133
|
+
/** Why this landing is right for this door. Prose, read by whoever adds the next door. Must not be empty. */
|
|
134
|
+
readonly because: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Options for {@link createSessionLandingDeclaredRule}.
|
|
138
|
+
*
|
|
139
|
+
* Ported from Settly, which hardcoded the two door calls, its six doors and the
|
|
140
|
+
* three landings (`login-result`, which must return `Promise<ILoginResult>`,
|
|
141
|
+
* `reissue` and `staff-only`). All of them are options; with no `doorCalls` the
|
|
142
|
+
* rule is inert.
|
|
143
|
+
*/
|
|
144
|
+
interface SessionLandingDeclaredOptions extends SessionSourceScopeOptions {
|
|
145
|
+
/** Rule id, for running more than one instance. Default `session-landing-declared`. */
|
|
146
|
+
readonly id?: string;
|
|
147
|
+
/**
|
|
148
|
+
* The methods whose call opens a door into a session (the mint, and the gate
|
|
149
|
+
* in front of it), each matched as `.<name>(`. Empty (the default) = inert.
|
|
150
|
+
*/
|
|
151
|
+
readonly doorCalls?: readonly string[];
|
|
152
|
+
/** Every door, declared. A file that calls a door method and is not here is reported. */
|
|
153
|
+
readonly doors?: readonly SessionDoor[];
|
|
154
|
+
/**
|
|
155
|
+
* The landings a door may declare, each mapped to the text a door with that
|
|
156
|
+
* landing must contain (the return type that carries the principal kind to the
|
|
157
|
+
* client, say), or `null` when the landing demands nothing of the source. A
|
|
158
|
+
* door whose landing is not a key here is reported. Default: none.
|
|
159
|
+
*/
|
|
160
|
+
readonly landings?: Readonly<Record<string, string | null>>;
|
|
161
|
+
/** Appended to every message: a pointer to the project's own docs. */
|
|
162
|
+
readonly hint?: string;
|
|
163
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
164
|
+
readonly ciCritical?: boolean;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Every file that opens a door into a session declares where it leaves the
|
|
168
|
+
* caller, and a door that must hand the client the principal kind does.
|
|
169
|
+
*
|
|
170
|
+
* With two shells behind one sign-in, the client can only send an account to
|
|
171
|
+
* the right one if the response that ends the sign-in says which kind it is. A
|
|
172
|
+
* second door that finishes a sign-in from a different service can forget to,
|
|
173
|
+
* and nothing fails: the account signs in and lands in the wrong shell.
|
|
174
|
+
*
|
|
175
|
+
* The check is completeness first: a file that calls a door method and is not
|
|
176
|
+
* in `doors` fails the build until someone classifies it, which is what keeps
|
|
177
|
+
* the list an enumeration rather than a docblock nobody updates. Then each
|
|
178
|
+
* door's landing is checked against `landings`, and a declared door that no
|
|
179
|
+
* longer exists (or that `sourceGlobs` do not reach) is reported so the list
|
|
180
|
+
* cannot go stale.
|
|
181
|
+
*/
|
|
182
|
+
declare function createSessionLandingDeclaredRule(options?: SessionLandingDeclaredOptions): IMetaRule;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Options for {@link createSessionMintCallersRule}.
|
|
186
|
+
*
|
|
187
|
+
* Ported from Settly's `establish-session-callers`, which hardcoded the method
|
|
188
|
+
* (`establishSession`), the gate (`beginOrEstablish`) and the five allowlisted
|
|
189
|
+
* files. All three are options here; with no `mintCall` the rule is inert.
|
|
190
|
+
*/
|
|
191
|
+
interface SessionMintCallersOptions extends SessionSourceScopeOptions {
|
|
192
|
+
/** Rule id, for running more than one instance. Default `session-mint-callers`. */
|
|
193
|
+
readonly id?: string;
|
|
194
|
+
/**
|
|
195
|
+
* The method whose call mints a session (sets the cookie, writes the store),
|
|
196
|
+
* matched as `.<mintCall>(`. Unset or empty = inert.
|
|
197
|
+
*/
|
|
198
|
+
readonly mintCall?: string;
|
|
199
|
+
/**
|
|
200
|
+
* Repo-relative files that may call it: the method's own home, the gate in
|
|
201
|
+
* front of it, and flows with no gate to pass (signup, a re-issue to a caller
|
|
202
|
+
* who already holds a session). Matched exactly, so a same-named file in
|
|
203
|
+
* another directory is not allowed. Default: none.
|
|
204
|
+
*/
|
|
205
|
+
readonly allowedCallers?: readonly string[];
|
|
206
|
+
/**
|
|
207
|
+
* The method a new sign-in entry point must route through instead (a second
|
|
208
|
+
* factor challenge, say), named in the message. Optional.
|
|
209
|
+
*/
|
|
210
|
+
readonly gateCall?: string;
|
|
211
|
+
/** Appended to every message: a pointer to the project's own docs. */
|
|
212
|
+
readonly hint?: string;
|
|
213
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
214
|
+
readonly ciCritical?: boolean;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The method that mints a session is callable only from an allowlist of files.
|
|
218
|
+
*
|
|
219
|
+
* A session minted straight from a sign-in entry point skips whatever the gate
|
|
220
|
+
* in front of the mint enforces (a second factor, an epoch capture), and nothing
|
|
221
|
+
* else fails: the new flow signs the user in and every test passes. The mint is
|
|
222
|
+
* the one door into a session, so the rule fences the door by caller.
|
|
223
|
+
*/
|
|
224
|
+
declare function createSessionMintCallersRule(options?: SessionMintCallersOptions): IMetaRule;
|
|
225
|
+
|
|
226
|
+
export { type SessionDoor, type SessionEpochCapturedOptions, type SessionKindStampedOptions, type SessionLandingDeclaredOptions, type SessionMintCallersOptions, type SessionSourceScopeOptions, callArguments, createSessionEpochCapturedRule, createSessionKindStampedRule, createSessionLandingDeclaredRule, createSessionMintCallersRule };
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { IMetaRule } from '@noctcore/harness';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The source scope every session rule shares: which files are read, and which
|
|
5
|
+
* of them are left alone because they drive the seam directly (tests).
|
|
6
|
+
*/
|
|
7
|
+
interface SessionSourceScopeOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Globs of the application source that can open a session, relative to the
|
|
10
|
+
* repo root. Empty (the default) makes the rule inert: it reads nothing and
|
|
11
|
+
* reports nothing. Point it at shipped code only; a lint-meta rule module that
|
|
12
|
+
* quotes the call in its own pattern is not a call site.
|
|
13
|
+
*/
|
|
14
|
+
readonly sourceGlobs?: readonly string[];
|
|
15
|
+
/** Paths with any of these segments are skipped. Default `node_modules`, `.git`, `dist`, `.turbo`, `coverage`. */
|
|
16
|
+
readonly skipDirs?: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Files ending in any of these are skipped: tests drive the seam directly,
|
|
19
|
+
* including shapes production never produces. Default
|
|
20
|
+
* `['.spec.ts', '.spec.tsx', '.test.ts', '.test.tsx']`.
|
|
21
|
+
*/
|
|
22
|
+
readonly excludeSuffixes?: readonly string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The argument text of each `.<method>(...)` call in `source`.
|
|
26
|
+
*
|
|
27
|
+
* Scanned by balancing parentheses rather than matched with a regex: an options
|
|
28
|
+
* object spans lines and holds its own braces and parens, and a non-greedy
|
|
29
|
+
* regex either stops early or runs on into the next call. A scanner that never
|
|
30
|
+
* finds the closing paren returns what it has, which is the fail-closed
|
|
31
|
+
* direction (more text to search, never less). `.<method>Something(` is not a
|
|
32
|
+
* call of `method`: only whitespace may sit between the name and the paren.
|
|
33
|
+
*/
|
|
34
|
+
declare function callArguments(source: string, method: string): string[];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Options for {@link createSessionEpochCapturedRule}.
|
|
38
|
+
*
|
|
39
|
+
* Ported from Settly, which hardcoded the seam (`beginOrEstablish`), the field
|
|
40
|
+
* (`epoch`) and the seam's own file. With no `call` the rule is inert.
|
|
41
|
+
*/
|
|
42
|
+
interface SessionEpochCapturedOptions extends SessionSourceScopeOptions {
|
|
43
|
+
/** Rule id, for running more than one instance. Default `session-epoch-captured`. */
|
|
44
|
+
readonly id?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The post-credential seam every sign-in passes through on its way to a
|
|
47
|
+
* session, matched as `.<call>(`. Unset or empty = inert.
|
|
48
|
+
*/
|
|
49
|
+
readonly call?: string;
|
|
50
|
+
/** The argument every call must mention: the epoch captured before the credential was read. Default `epoch`. */
|
|
51
|
+
readonly field?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Repo-relative files skipped outright: the seam's own home, which defines
|
|
54
|
+
* the method and may call itself without a captured value. Default: none.
|
|
55
|
+
*/
|
|
56
|
+
readonly exempt?: readonly string[];
|
|
57
|
+
/** How the epoch is captured (e.g. `sessions.readEpoch(userId)`), quoted in the message. Optional. */
|
|
58
|
+
readonly captureCall?: string;
|
|
59
|
+
/** Appended to every message: a pointer to the project's own docs. */
|
|
60
|
+
readonly hint?: string;
|
|
61
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
62
|
+
readonly ciCritical?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Every call into the sign-in seam passes the session epoch it captured before
|
|
66
|
+
* reading the credential.
|
|
67
|
+
*
|
|
68
|
+
* A revoke-all bumps a per-user epoch and a mint refuses to write a session
|
|
69
|
+
* under a stale one. Captured inside the mint, the fence covers the store write
|
|
70
|
+
* and nothing else: the whole credential check (a password hash verify, an
|
|
71
|
+
* OAuth token exchange) is a window in which a sign-in that already read the
|
|
72
|
+
* revoked state still mints a surviving session. So each entry point captures
|
|
73
|
+
* the epoch first and hands it to the seam, and this rule fails any call whose
|
|
74
|
+
* arguments never mention it.
|
|
75
|
+
*/
|
|
76
|
+
declare function createSessionEpochCapturedRule(options?: SessionEpochCapturedOptions): IMetaRule;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Options for {@link createSessionKindStampedRule}.
|
|
80
|
+
*
|
|
81
|
+
* Ported from Settly, which hardcoded the mint (`establishSession`), the field
|
|
82
|
+
* (`kind`) and the one staff-only exemption. With no `mintCall` the rule is inert.
|
|
83
|
+
*/
|
|
84
|
+
interface SessionKindStampedOptions extends SessionSourceScopeOptions {
|
|
85
|
+
/** Rule id, for running more than one instance. Default `session-kind-stamped`. */
|
|
86
|
+
readonly id?: string;
|
|
87
|
+
/** The method whose call mints a session, matched as `.<mintCall>(`. Unset or empty = inert. */
|
|
88
|
+
readonly mintCall?: string;
|
|
89
|
+
/** The session field every mint must stamp. Default `kind`. */
|
|
90
|
+
readonly field?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Repo-relative files whose mints may stamp nothing because they provably can
|
|
93
|
+
* never mint for a principal that needs the field (a self-signup that only
|
|
94
|
+
* ever creates the default kind). Keep it short and write the proof next to
|
|
95
|
+
* each entry. Default: none.
|
|
96
|
+
*/
|
|
97
|
+
readonly allowUnstamped?: readonly string[];
|
|
98
|
+
/** An example of the stamp, quoted in the message (e.g. a conditional spread). Optional. */
|
|
99
|
+
readonly stampExample?: string;
|
|
100
|
+
/** Appended to every message: a pointer to the project's own docs. */
|
|
101
|
+
readonly hint?: string;
|
|
102
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
103
|
+
readonly ciCritical?: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Every session mint stamps the principal's kind onto the session it writes.
|
|
107
|
+
*
|
|
108
|
+
* When the request pipeline reads the kind from the SESSION rather than the
|
|
109
|
+
* database, a session minted without it reads as whatever the reader defaults
|
|
110
|
+
* to. For a fence between two kinds of account that is a silent promotion of
|
|
111
|
+
* one into the other, and every gate stays green because nothing looks.
|
|
112
|
+
*
|
|
113
|
+
* The check, in order of precision:
|
|
114
|
+
*
|
|
115
|
+
* 1. A call whose arguments contain an object LITERAL must mention the field in
|
|
116
|
+
* that literal. Each literal is checked on its own, so one stamped mint does
|
|
117
|
+
* not excuse an unstamped one in the same file.
|
|
118
|
+
* 2. A call with no literal (options built elsewhere) falls back to the file:
|
|
119
|
+
* the file must mention the field somewhere. Coarser, but no silent hole.
|
|
120
|
+
* 3. Otherwise the file must be on `allowUnstamped`.
|
|
121
|
+
*
|
|
122
|
+
* Residual gap, stated rather than hidden: a file that stamps the field on one
|
|
123
|
+
* delegated mint and forgets it on a second delegated mint passes clause 2.
|
|
124
|
+
*/
|
|
125
|
+
declare function createSessionKindStampedRule(options?: SessionKindStampedOptions): IMetaRule;
|
|
126
|
+
|
|
127
|
+
/** One file that opens a door into a session, and where it leaves the caller. */
|
|
128
|
+
interface SessionDoor {
|
|
129
|
+
/** Repo-relative path of the file. */
|
|
130
|
+
readonly file: string;
|
|
131
|
+
/** One of the keys of `landings`. */
|
|
132
|
+
readonly landing: string;
|
|
133
|
+
/** Why this landing is right for this door. Prose, read by whoever adds the next door. Must not be empty. */
|
|
134
|
+
readonly because: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Options for {@link createSessionLandingDeclaredRule}.
|
|
138
|
+
*
|
|
139
|
+
* Ported from Settly, which hardcoded the two door calls, its six doors and the
|
|
140
|
+
* three landings (`login-result`, which must return `Promise<ILoginResult>`,
|
|
141
|
+
* `reissue` and `staff-only`). All of them are options; with no `doorCalls` the
|
|
142
|
+
* rule is inert.
|
|
143
|
+
*/
|
|
144
|
+
interface SessionLandingDeclaredOptions extends SessionSourceScopeOptions {
|
|
145
|
+
/** Rule id, for running more than one instance. Default `session-landing-declared`. */
|
|
146
|
+
readonly id?: string;
|
|
147
|
+
/**
|
|
148
|
+
* The methods whose call opens a door into a session (the mint, and the gate
|
|
149
|
+
* in front of it), each matched as `.<name>(`. Empty (the default) = inert.
|
|
150
|
+
*/
|
|
151
|
+
readonly doorCalls?: readonly string[];
|
|
152
|
+
/** Every door, declared. A file that calls a door method and is not here is reported. */
|
|
153
|
+
readonly doors?: readonly SessionDoor[];
|
|
154
|
+
/**
|
|
155
|
+
* The landings a door may declare, each mapped to the text a door with that
|
|
156
|
+
* landing must contain (the return type that carries the principal kind to the
|
|
157
|
+
* client, say), or `null` when the landing demands nothing of the source. A
|
|
158
|
+
* door whose landing is not a key here is reported. Default: none.
|
|
159
|
+
*/
|
|
160
|
+
readonly landings?: Readonly<Record<string, string | null>>;
|
|
161
|
+
/** Appended to every message: a pointer to the project's own docs. */
|
|
162
|
+
readonly hint?: string;
|
|
163
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
164
|
+
readonly ciCritical?: boolean;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Every file that opens a door into a session declares where it leaves the
|
|
168
|
+
* caller, and a door that must hand the client the principal kind does.
|
|
169
|
+
*
|
|
170
|
+
* With two shells behind one sign-in, the client can only send an account to
|
|
171
|
+
* the right one if the response that ends the sign-in says which kind it is. A
|
|
172
|
+
* second door that finishes a sign-in from a different service can forget to,
|
|
173
|
+
* and nothing fails: the account signs in and lands in the wrong shell.
|
|
174
|
+
*
|
|
175
|
+
* The check is completeness first: a file that calls a door method and is not
|
|
176
|
+
* in `doors` fails the build until someone classifies it, which is what keeps
|
|
177
|
+
* the list an enumeration rather than a docblock nobody updates. Then each
|
|
178
|
+
* door's landing is checked against `landings`, and a declared door that no
|
|
179
|
+
* longer exists (or that `sourceGlobs` do not reach) is reported so the list
|
|
180
|
+
* cannot go stale.
|
|
181
|
+
*/
|
|
182
|
+
declare function createSessionLandingDeclaredRule(options?: SessionLandingDeclaredOptions): IMetaRule;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Options for {@link createSessionMintCallersRule}.
|
|
186
|
+
*
|
|
187
|
+
* Ported from Settly's `establish-session-callers`, which hardcoded the method
|
|
188
|
+
* (`establishSession`), the gate (`beginOrEstablish`) and the five allowlisted
|
|
189
|
+
* files. All three are options here; with no `mintCall` the rule is inert.
|
|
190
|
+
*/
|
|
191
|
+
interface SessionMintCallersOptions extends SessionSourceScopeOptions {
|
|
192
|
+
/** Rule id, for running more than one instance. Default `session-mint-callers`. */
|
|
193
|
+
readonly id?: string;
|
|
194
|
+
/**
|
|
195
|
+
* The method whose call mints a session (sets the cookie, writes the store),
|
|
196
|
+
* matched as `.<mintCall>(`. Unset or empty = inert.
|
|
197
|
+
*/
|
|
198
|
+
readonly mintCall?: string;
|
|
199
|
+
/**
|
|
200
|
+
* Repo-relative files that may call it: the method's own home, the gate in
|
|
201
|
+
* front of it, and flows with no gate to pass (signup, a re-issue to a caller
|
|
202
|
+
* who already holds a session). Matched exactly, so a same-named file in
|
|
203
|
+
* another directory is not allowed. Default: none.
|
|
204
|
+
*/
|
|
205
|
+
readonly allowedCallers?: readonly string[];
|
|
206
|
+
/**
|
|
207
|
+
* The method a new sign-in entry point must route through instead (a second
|
|
208
|
+
* factor challenge, say), named in the message. Optional.
|
|
209
|
+
*/
|
|
210
|
+
readonly gateCall?: string;
|
|
211
|
+
/** Appended to every message: a pointer to the project's own docs. */
|
|
212
|
+
readonly hint?: string;
|
|
213
|
+
/** Whether a violation fails CI. Default `true`. */
|
|
214
|
+
readonly ciCritical?: boolean;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The method that mints a session is callable only from an allowlist of files.
|
|
218
|
+
*
|
|
219
|
+
* A session minted straight from a sign-in entry point skips whatever the gate
|
|
220
|
+
* in front of the mint enforces (a second factor, an epoch capture), and nothing
|
|
221
|
+
* else fails: the new flow signs the user in and every test passes. The mint is
|
|
222
|
+
* the one door into a session, so the rule fences the door by caller.
|
|
223
|
+
*/
|
|
224
|
+
declare function createSessionMintCallersRule(options?: SessionMintCallersOptions): IMetaRule;
|
|
225
|
+
|
|
226
|
+
export { type SessionDoor, type SessionEpochCapturedOptions, type SessionKindStampedOptions, type SessionLandingDeclaredOptions, type SessionMintCallersOptions, type SessionSourceScopeOptions, callArguments, createSessionEpochCapturedRule, createSessionKindStampedRule, createSessionLandingDeclaredRule, createSessionMintCallersRule };
|