@cosmicdrift/kumiko-guards 0.338.0 → 0.339.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.339.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>",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"kumiko-pre-push": "./src/pre-push.sh"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@cosmicdrift/kumiko-repo-manifest": "0.
|
|
31
|
+
"@cosmicdrift/kumiko-repo-manifest": "0.339.0",
|
|
32
32
|
"ts-morph": "^28.0.0"
|
|
33
33
|
},
|
|
34
34
|
"publishConfig": {
|
package/src/_lib/guard-kit.ts
CHANGED
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
* kumiko.json manifest instead of hardcoded repo-kind globs.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
|
-
import { relative as pathRelative, resolve } from "node:path";
|
|
15
|
+
import { existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
16
|
+
import { relative as pathRelative, resolve, sep } from "node:path";
|
|
17
17
|
import { Project, type SourceFile } from "ts-morph";
|
|
18
18
|
import { compareToBaseline, findRepoRootFor } from "./baseline-compare";
|
|
19
19
|
import {
|
|
20
20
|
explainRepoRoots,
|
|
21
|
+
findLocalRepo,
|
|
21
22
|
frameworkTsConfigPath,
|
|
22
23
|
type RepoRoot,
|
|
23
24
|
type RootResolution,
|
|
@@ -62,6 +63,49 @@ export function isSecurityGuard(guard: Pick<AstGuard, "security">): boolean {
|
|
|
62
63
|
return guard.security === true;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
// Followers call the predicate per import hop; findLocalRepo re-reads
|
|
67
|
+
// package.json and the manifest each time.
|
|
68
|
+
const localRepoRootByCwd = new Map<string, string | undefined>();
|
|
69
|
+
|
|
70
|
+
function localRepoRoot(): string | undefined {
|
|
71
|
+
const cwd = process.cwd();
|
|
72
|
+
if (!localRepoRootByCwd.has(cwd)) localRepoRootByCwd.set(cwd, findLocalRepo(cwd)?.absPath);
|
|
73
|
+
return localRepoRootByCwd.get(cwd);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function realOrResolved(path: string): string {
|
|
77
|
+
try {
|
|
78
|
+
return realpathSync(path);
|
|
79
|
+
} catch {
|
|
80
|
+
// In-memory test files and paths not on disk: compare as spelled.
|
|
81
|
+
return resolve(path);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* True for a file outside the local repo's root — a neighbour repo of the
|
|
87
|
+
* parent workspace reached through a workspace symlink or a `../..` import. Such
|
|
88
|
+
* a file is as external as a package under node_modules: a guard may not scan it
|
|
89
|
+
* or follow imports into it, or a `.wt/<repo>` worktree reports findings that a
|
|
90
|
+
* fresh CI checkout (no parent workspace) never sees. The root comes from
|
|
91
|
+
* `findLocalRepo`, not a path heuristic; without a local repo nothing is
|
|
92
|
+
* decidable and nothing counts as outside.
|
|
93
|
+
*/
|
|
94
|
+
export function isOutsideRepoRoot(
|
|
95
|
+
filePath: string,
|
|
96
|
+
repoRoot: string | undefined = localRepoRoot(),
|
|
97
|
+
): boolean {
|
|
98
|
+
if (repoRoot === undefined) return false;
|
|
99
|
+
const root = realOrResolved(repoRoot);
|
|
100
|
+
const file = realOrResolved(filePath);
|
|
101
|
+
return file !== root && !file.startsWith(root + sep);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Import followers stop at these: a package from node_modules or a file outside the repo root. */
|
|
105
|
+
export function isExternalSourcePath(filePath: string): boolean {
|
|
106
|
+
return filePath.includes("/node_modules/") || isOutsideRepoRoot(filePath);
|
|
107
|
+
}
|
|
108
|
+
|
|
65
109
|
export function buildSharedProject(
|
|
66
110
|
guards: readonly AstGuard[],
|
|
67
111
|
roots: readonly RepoRoot[] = resolveRepoRoots(),
|
|
@@ -88,7 +132,7 @@ export function buildSharedProject(
|
|
|
88
132
|
}
|
|
89
133
|
}),
|
|
90
134
|
),
|
|
91
|
-
];
|
|
135
|
+
].filter((path) => roots.some((root) => !isOutsideRepoRoot(path, root.absPath)));
|
|
92
136
|
// Exact paths, never re-glob: filenames like `[id].tsx` are literal here.
|
|
93
137
|
for (const path of union) project.addSourceFileAtPath(path);
|
|
94
138
|
return project;
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.339.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Guards ignore neighbour repos of the parent workspace",
|
|
6
|
+
"detail": "The root comes from `findLocalRepo`, not a path heuristic. `isOutsideRepoRoot(filePath, repoRoot?)` compares real paths, so symlinks into a neighbour repo count as outside. `buildSharedProject` drops such files and `no-framed-extension-sections` stops following imports into them.",
|
|
7
|
+
"migration": "keine"
|
|
8
|
+
},
|
|
2
9
|
{
|
|
3
10
|
"version": "0.337.0",
|
|
4
11
|
"type": "fix",
|
|
@@ -41,7 +41,13 @@ import {
|
|
|
41
41
|
SyntaxKind,
|
|
42
42
|
VariableDeclarationKind,
|
|
43
43
|
} from "ts-morph";
|
|
44
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
type AstGuard,
|
|
46
|
+
type GuardViolation,
|
|
47
|
+
isExternalSourcePath,
|
|
48
|
+
runStandalone,
|
|
49
|
+
type ScanSpec,
|
|
50
|
+
} from "./_lib/guard-kit";
|
|
45
51
|
import { hasIgnoreTag } from "./_lib/ignore-tag";
|
|
46
52
|
|
|
47
53
|
const ROOT = process.cwd();
|
|
@@ -58,11 +64,12 @@ const MAX_HOPS = 4;
|
|
|
58
64
|
const FRAMED_TAG = /^(Card|SectionCard|CollapsibleSection)$/;
|
|
59
65
|
|
|
60
66
|
// A vendored package's shipped .tsx source is still resolvable through
|
|
61
|
-
// node_modules
|
|
67
|
+
// node_modules, and in a parent workspace a workspace link resolves into a
|
|
68
|
+
// neighbour repo — treat both the same as a truly unresolvable specifier
|
|
62
69
|
// ("package component, checked inside the package itself"), not as a hop
|
|
63
70
|
// into the App-Repo's own extension code.
|
|
64
71
|
function isExternalSourceFile(sf: SourceFile): boolean {
|
|
65
|
-
return sf.getFilePath()
|
|
72
|
+
return isExternalSourcePath(sf.getFilePath());
|
|
66
73
|
}
|
|
67
74
|
|
|
68
75
|
function stringConstantValue(node: Node): string | undefined {
|