@cosmicdrift/kumiko-guards 0.288.0 → 0.289.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-guards",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.289.0",
|
|
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.289.0",
|
|
31
31
|
"ts-morph": "^28.0.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* I/O: Bun.Glob + Bun.file; directoryExists uses node:fs (same as roots.ts).
|
|
12
12
|
*/
|
|
13
13
|
import { existsSync, statSync } from "node:fs";
|
|
14
|
-
import type
|
|
14
|
+
import { type RepoRoot, sourceRootDirs } from "./roots";
|
|
15
15
|
|
|
16
16
|
/** POSIX path join without Node path module. */
|
|
17
17
|
function joinPath(base: string, ...segments: string[]): string {
|
|
@@ -107,20 +107,10 @@ export const BLOCKING_SQL_KINDS: ReadonlyArray<SqlInventoryKind> = ["unsafe", "a
|
|
|
107
107
|
|
|
108
108
|
const TS_GLOB = new Bun.Glob("**/*.{ts,tsx}");
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (root.name === "kumiko-platform") return "none";
|
|
115
|
-
if (root.kind === "framework" || root.kind === "library") return "multi-package";
|
|
116
|
-
return "flat";
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/** Scan dirs per repo layout — multi-package keeps the packages/samples/scripts/bin layout, flat is a bare src/ + bin/. */
|
|
120
|
-
function scanDirsFor(layout: SqlScanLayout): readonly string[] {
|
|
121
|
-
if (layout === "multi-package") return ["packages", "samples", "scripts", "bin"];
|
|
122
|
-
if (layout === "none") return [];
|
|
123
|
-
return ["src", "bin"];
|
|
110
|
+
// kumiko-platform deliberately returns no scan dirs (0 files) — its docs-samples tree needs its own allowlist review before this guard scans it (follow-up issue).
|
|
111
|
+
export function sqlScanDirsFor(root: RepoRoot): readonly string[] {
|
|
112
|
+
if (root.name === "kumiko-platform") return [];
|
|
113
|
+
return sourceRootDirs(root).map((dir) => dir.slice(root.absPath.length).replace(/^\/+/, ""));
|
|
124
114
|
}
|
|
125
115
|
|
|
126
116
|
function normalizePathForMatch(filePath: string): string {
|
|
@@ -155,9 +145,9 @@ function directoryExists(path: string): boolean {
|
|
|
155
145
|
}
|
|
156
146
|
}
|
|
157
147
|
|
|
158
|
-
async function collectTsFiles(repoRoot: string,
|
|
148
|
+
async function collectTsFiles(repoRoot: string, scanDirs: readonly string[]): Promise<string[]> {
|
|
159
149
|
const out: string[] = [];
|
|
160
|
-
for (const sub of
|
|
150
|
+
for (const sub of scanDirs) {
|
|
161
151
|
const cwd = joinPath(repoRoot, sub);
|
|
162
152
|
if (!directoryExists(cwd)) continue;
|
|
163
153
|
for await (const rel of TS_GLOB.scan({ cwd, onlyFiles: true })) {
|
|
@@ -226,9 +216,9 @@ function scanFileText(relPath: string, text: string, hits: SqlInventoryHit[]): v
|
|
|
226
216
|
|
|
227
217
|
export async function scanRepo(
|
|
228
218
|
repoRoot: string,
|
|
229
|
-
|
|
219
|
+
scanDirs: readonly string[],
|
|
230
220
|
): Promise<SqlInventoryReport> {
|
|
231
|
-
const relFiles = await collectTsFiles(repoRoot,
|
|
221
|
+
const relFiles = await collectTsFiles(repoRoot, scanDirs);
|
|
232
222
|
const hits: SqlInventoryHit[] = [];
|
|
233
223
|
|
|
234
224
|
for (const rel of relFiles) {
|
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
runRepoChecks,
|
|
64
64
|
} from "./_lib/guard-kit";
|
|
65
65
|
import { frameworkTsConfigPath, type RepoRoot, resolveRepoRoots } from "./_lib/roots";
|
|
66
|
-
import { type ScanSpec, scanFiles } from "./_lib/scan-scope";
|
|
66
|
+
import { type ScanSpec, scanFiles, scanRoots } from "./_lib/scan-scope";
|
|
67
67
|
import {
|
|
68
68
|
classify,
|
|
69
69
|
computeClientReachablePaths,
|
|
@@ -166,6 +166,12 @@ export const check: RepoCheck = {
|
|
|
166
166
|
run(roots) {
|
|
167
167
|
const root = roots[0];
|
|
168
168
|
if (!root) return { violations: [], matchedFiles: 0, notApplicable: true };
|
|
169
|
+
// The kind filter (scan-scope.ts keepsRootKind) excludes "tooling" roots by
|
|
170
|
+
// default — that's a deliberate scope choice, not a vacuous scan, so it must
|
|
171
|
+
// report notApplicable instead of matchedFiles: 0.
|
|
172
|
+
if (scanRoots(SCAN, [root]).length === 0) {
|
|
173
|
+
return { violations: [], matchedFiles: 0, notApplicable: true };
|
|
174
|
+
}
|
|
169
175
|
const rootAbsPath = root.absPath;
|
|
170
176
|
|
|
171
177
|
const { allViolations, outsideRoot, scannedFiles } = scanRoot(root);
|
|
@@ -28,13 +28,12 @@ import { type RepoCheck, reportResults, runRepoChecks } from "./_lib/guard-kit";
|
|
|
28
28
|
import { type RepoRoot, resolveRepoRoots } from "./_lib/roots";
|
|
29
29
|
import { scanLinesForPredicate } from "./_lib/scan-lines";
|
|
30
30
|
|
|
31
|
-
// Server-side only:
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
];
|
|
31
|
+
// Server-side only: a declared sourceRoot under a mobile/client app has no server secrets to leak.
|
|
32
|
+
const CLIENT_SOURCE_ROOT = /(?:^|\/)mobile(?:\/|$)/;
|
|
33
|
+
|
|
34
|
+
function serverSourceRoots(root: RepoRoot): readonly string[] {
|
|
35
|
+
return root.manifest.sourceRoots.filter((sourceRoot) => !CLIENT_SOURCE_ROOT.test(sourceRoot));
|
|
36
|
+
}
|
|
38
37
|
|
|
39
38
|
const EXCLUDE_DIR = /(?:^|\/)(?:node_modules|dist|__tests__)\//;
|
|
40
39
|
const IS_TEST = /\.(?:test|integration)\.tsx?$/;
|
|
@@ -75,8 +74,8 @@ async function scanRoot(
|
|
|
75
74
|
): Promise<{ readonly findings: SecretLiteralFinding[]; readonly scannedFiles: number }> {
|
|
76
75
|
const findings: SecretLiteralFinding[] = [];
|
|
77
76
|
let scannedFiles = 0;
|
|
78
|
-
for (const
|
|
79
|
-
for (const rel of new Glob(
|
|
77
|
+
for (const sourceRoot of serverSourceRoots(root)) {
|
|
78
|
+
for (const rel of new Glob(`${sourceRoot}/**/*.ts`).scanSync({ cwd: root.absPath })) {
|
|
80
79
|
if (EXCLUDE_DIR.test(`/${rel}`) || IS_TEST.test(rel) || DEV_ENTRYPOINT.test(rel)) {
|
|
81
80
|
continue;
|
|
82
81
|
}
|
package/src/guard-raw-sql.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { type RepoCheck, reportResults, runRepoChecks } from "./_lib/guard-kit";
|
|
16
16
|
import { type RepoRoot, resolveRepoRoots } from "./_lib/roots";
|
|
17
|
-
import { BLOCKING_SQL_KINDS, scanRepo,
|
|
17
|
+
import { BLOCKING_SQL_KINDS, scanRepo, sqlScanDirsFor } from "./_lib/sql-inventory";
|
|
18
18
|
|
|
19
19
|
export type RawSqlFinding = {
|
|
20
20
|
readonly repo: string;
|
|
@@ -32,7 +32,7 @@ async function scanAllRepos(roots: readonly RepoRoot[]): Promise<{
|
|
|
32
32
|
let scannedFiles = 0;
|
|
33
33
|
|
|
34
34
|
for (const root of roots) {
|
|
35
|
-
const report = await scanRepo(root.absPath,
|
|
35
|
+
const report = await scanRepo(root.absPath, sqlScanDirsFor(root));
|
|
36
36
|
scannedFiles += report.scannedFiles;
|
|
37
37
|
|
|
38
38
|
for (const hit of report.hits) {
|
|
@@ -66,7 +66,7 @@ export const check: RepoCheck = {
|
|
|
66
66
|
"// kumiko-lint-ignore raw-sql <reason> on the line or the line above.",
|
|
67
67
|
async run(roots) {
|
|
68
68
|
// kumiko-platform's deliberate empty scan-dir list must not read as vacuous (infra#610).
|
|
69
|
-
const applicableRoots = roots.filter((r) =>
|
|
69
|
+
const applicableRoots = roots.filter((r) => sqlScanDirsFor(r).length > 0);
|
|
70
70
|
if (applicableRoots.length === 0) {
|
|
71
71
|
return { violations: [], matchedFiles: 0, notApplicable: true };
|
|
72
72
|
}
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
} from "ts-morph";
|
|
40
40
|
import { type RepoCheck, reportResults, runRepoChecks } from "./_lib/guard-kit";
|
|
41
41
|
import { frameworkTsConfigPath } from "./_lib/roots";
|
|
42
|
-
import { type ScanSpec, scanFiles } from "./_lib/scan-scope";
|
|
42
|
+
import { type ScanSpec, scanFiles, scanRoots } from "./_lib/scan-scope";
|
|
43
43
|
|
|
44
44
|
const ROOT = process.cwd();
|
|
45
45
|
|
|
@@ -385,6 +385,13 @@ const SCAN: ScanSpec = {
|
|
|
385
385
|
export const check: RepoCheck = {
|
|
386
386
|
name: "Thin-Wrappers Guard",
|
|
387
387
|
run(roots) {
|
|
388
|
+
// The kind filter (scan-scope.ts keepsRootKind) excludes "tooling" roots by
|
|
389
|
+
// default — that's a deliberate scope choice, not a vacuous scan, so it must
|
|
390
|
+
// report notApplicable instead of matchedFiles: 0.
|
|
391
|
+
if (scanRoots(SCAN, roots).length === 0) {
|
|
392
|
+
return { violations: [], matchedFiles: 0, notApplicable: true };
|
|
393
|
+
}
|
|
394
|
+
|
|
388
395
|
const project = new Project({
|
|
389
396
|
tsConfigFilePath: frameworkTsConfigPath(),
|
|
390
397
|
skipAddingFilesFromTsConfig: true,
|
|
@@ -416,7 +423,7 @@ export const check: RepoCheck = {
|
|
|
416
423
|
violations: [],
|
|
417
424
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
418
425
|
matchedFiles: scanned,
|
|
419
|
-
notApplicable:
|
|
426
|
+
notApplicable: false,
|
|
420
427
|
};
|
|
421
428
|
},
|
|
422
429
|
};
|