@cosmicdrift/kumiko-guards 0.284.0 → 0.285.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-guards",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.285.1",
|
|
4
4
|
"description": "AST-based security guards for Kumiko repos: direct-fs/fetch, tenant escalation, admin-API, escape hatches and related checks, run over a shared ts-morph project.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"kumiko-guards": "./src/cli.ts"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@cosmicdrift/kumiko-repo-manifest": "0.
|
|
30
|
+
"@cosmicdrift/kumiko-repo-manifest": "0.285.1",
|
|
31
31
|
"ts-morph": "^28.0.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
package/src/_lib/scan-scope.ts
CHANGED
|
@@ -9,7 +9,8 @@ export type ScanExtension = "ts" | "tsx";
|
|
|
9
9
|
|
|
10
10
|
type ScanSpecBase = {
|
|
11
11
|
readonly extensions: readonly ScanExtension[];
|
|
12
|
-
/** Manifest roles to scan; omitted = every root
|
|
12
|
+
/** Manifest roles to scan; omitted = every root except "tooling" — a guard
|
|
13
|
+
* must opt in to scanning an infra/build-tooling root explicitly. */
|
|
13
14
|
readonly kinds?: readonly RepoKind[];
|
|
14
15
|
/** Repo-relative globs; in a kind "framework" root only files matching one of them are kept (replaces `within` there). */
|
|
15
16
|
readonly frameworkWithin?: readonly string[];
|
|
@@ -141,10 +142,13 @@ function scanRoot(spec: ScanSpec, root: RepoRoot): RootScan {
|
|
|
141
142
|
return { root, files, sourceSurface: sourceSurfaceHits.length };
|
|
142
143
|
}
|
|
143
144
|
|
|
145
|
+
function keepsRootKind(spec: ScanSpec, root: RepoRoot): boolean {
|
|
146
|
+
if (spec.kinds) return spec.kinds.includes(root.kind);
|
|
147
|
+
return root.kind !== "tooling";
|
|
148
|
+
}
|
|
149
|
+
|
|
144
150
|
export function scanRoots(spec: ScanSpec, roots: readonly RepoRoot[]): RootScan[] {
|
|
145
|
-
return roots
|
|
146
|
-
.filter((root) => !spec.kinds || spec.kinds.includes(root.kind))
|
|
147
|
-
.map((root) => scanRoot(spec, root));
|
|
151
|
+
return roots.filter((root) => keepsRootKind(spec, root)).map((root) => scanRoot(spec, root));
|
|
148
152
|
}
|
|
149
153
|
|
|
150
154
|
export function scanFiles(spec: ScanSpec, roots: readonly RepoRoot[]): string[] {
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.285.1",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Fix false-positive BLOCK in the Direct-Entity-Writes Guard for repos with no event store",
|
|
6
|
+
"detail": "An empty `esTables` set had one meaning: the scan lost track of ES definitions while table-write code still exists, so the guard reported a misconfiguration canary and blocked (the guard is `security: true`, so this always failed CI). That conflated two different situations: a repo with no event store at all (e.g. `kumiko-platform`, an Astro docs/marketing site) also has an empty `esTables` set, but there is nothing to guard against there. The guard now checks whether any table write is present at all before treating an empty `esTables` set as a misconfiguration; a repo with no ES and no table writes is green, while a repo with table writes but no resolvable ES tables still blocks as before."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.284.0",
|
|
4
10
|
"type": "fix",
|
|
@@ -287,6 +287,16 @@ function resolveWrite(
|
|
|
287
287
|
return undefined;
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
function hasAnyTableWrite(files: readonly SourceFile[]): boolean {
|
|
291
|
+
for (const sf of files) {
|
|
292
|
+
if (EXCLUDE.test(sf.getFilePath())) continue;
|
|
293
|
+
for (const call of sf.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
294
|
+
if (resolveWrite(call)) return true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
|
|
290
300
|
export function scanDirectWrites(
|
|
291
301
|
sf: SourceFile,
|
|
292
302
|
esTables: ReadonlySet<TableId>,
|
|
@@ -351,13 +361,16 @@ export const guard: AstGuard = {
|
|
|
351
361
|
...collectEntityProjectionTables(files),
|
|
352
362
|
]);
|
|
353
363
|
if (esTables.size === 0) {
|
|
364
|
+
// An empty set means either the repo has no event store at all, or the
|
|
365
|
+
// scan lost the ES definitions while table writes are still present.
|
|
366
|
+
if (!hasAnyTableWrite(files)) return { violations: [] };
|
|
354
367
|
return {
|
|
355
368
|
violations: [
|
|
356
369
|
{
|
|
357
370
|
file: "<scan>",
|
|
358
371
|
line: 0,
|
|
359
372
|
message:
|
|
360
|
-
"BLOCKED: guard found no createEventStoreExecutor or r.entity projection tables — scan is probably misconfigured.",
|
|
373
|
+
"BLOCKED: guard found table writes but no createEventStoreExecutor or r.entity projection tables — scan is probably misconfigured.",
|
|
361
374
|
},
|
|
362
375
|
],
|
|
363
376
|
};
|