@cosmicdrift/kumiko-guards 0.330.2 → 0.332.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 +2 -2
- package/src/_lib/git-env.ts +27 -1
- package/src/_lib/guard-kit.ts +3 -2
- package/src/_lib/scan-scope.ts +14 -4
- package/src/_lib/security-baseline.ts +6 -2
- package/src/_lib/test-file.ts +2 -0
- package/src/changes.json +51 -0
- package/src/check-real-provider-isolation.ts +45 -11
- package/src/check-secret-literals.ts +95 -20
- package/src/guard-access-denied-test.ts +4 -4
- package/src/guard-admin-api.ts +11 -5
- package/src/guard-direct-entity-writes.ts +37 -1
- package/src/guard-direct-fetch.ts +4 -7
- package/src/guard-escape-hatch-declared.ts +2 -1
- package/src/guard-feature-integration-tests.ts +10 -9
- package/src/guard-html-escape.ts +76 -19
- package/src/guard-lib-test-coverage.ts +10 -3
- package/src/guard-loadall-events.ts +8 -2
- package/src/guard-no-direct-process-env.ts +2 -1
- package/src/guard-no-framed-extension-sections.ts +5 -1
- package/src/guard-pii-annotations.ts +5 -4
- package/src/guard-primitives-discipline.ts +52 -20
- package/src/guard-raw-interactive-elements.ts +25 -15
- package/src/guard-tailwind-scan-surface.ts +39 -28
- package/src/guard-tenant-escalation.ts +7 -8
- package/src/guard-test-stack-drift.ts +15 -2
- package/src/guard-text-field-stance.ts +4 -3
- package/src/guard-thin-wrappers.ts +2 -3
- package/src/index.ts +1 -0
- package/src/pre-push.sh +26 -4
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import * as path from "node:path";
|
|
20
|
-
import { Project, type SourceFile, SyntaxKind } from "ts-morph";
|
|
20
|
+
import { type Identifier, Node, Project, type SourceFile, SyntaxKind } from "ts-morph";
|
|
21
21
|
import {
|
|
22
22
|
type GuardViolation,
|
|
23
23
|
type RepoCheck,
|
|
@@ -70,10 +70,23 @@ export interface Violation {
|
|
|
70
70
|
forbiddenCalls: Array<{ name: string; line: number }>;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// `import { setupTestStack as stack }` + `stack()` must resolve to the imported
|
|
74
|
+
// name, otherwise an alias hides both entrypoints and forbidden factories.
|
|
75
|
+
function importedNameOf(identifier: Identifier): string {
|
|
76
|
+
const localName = identifier.getText();
|
|
77
|
+
for (const imp of identifier.getSourceFile().getImportDeclarations()) {
|
|
78
|
+
for (const spec of imp.getNamedImports()) {
|
|
79
|
+
if ((spec.getAliasNode()?.getText() ?? spec.getName()) === localName) return spec.getName();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return localName;
|
|
83
|
+
}
|
|
84
|
+
|
|
73
85
|
function collectCallNames(sf: SourceFile): Map<string, number[]> {
|
|
74
86
|
const calls = new Map<string, number[]>();
|
|
75
87
|
for (const call of sf.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
76
|
-
const
|
|
88
|
+
const callee = call.getExpression();
|
|
89
|
+
const name = Node.isIdentifier(callee) ? importedNameOf(callee) : callee.getText();
|
|
77
90
|
const arr = calls.get(name) ?? [];
|
|
78
91
|
arr.push(call.getStartLineNumber());
|
|
79
92
|
calls.set(name, arr);
|
|
@@ -144,9 +144,6 @@ function scanFieldFactories(sf: SourceFile, roots: readonly RepoRoot[]): Finding
|
|
|
144
144
|
const place = enclosingFieldName(call) ?? `${callee}(...)`;
|
|
145
145
|
const line = call.getStartLineNumber();
|
|
146
146
|
findings.push({ file, line, place, callee });
|
|
147
|
-
console.warn(
|
|
148
|
-
` [text-field-stance WARN] ${file}:${line} ${callee}(...) at "${place}" has no personal stance — mark ${VALID_PERSONAL_HINT}`,
|
|
149
|
-
);
|
|
150
147
|
}
|
|
151
148
|
return findings;
|
|
152
149
|
}
|
|
@@ -212,6 +209,10 @@ function analyse(
|
|
|
212
209
|
): GuardOutcome {
|
|
213
210
|
const { findings } = scan(files, roots);
|
|
214
211
|
if (!compareBaseline) {
|
|
212
|
+
for (const f of findings)
|
|
213
|
+
console.warn(
|
|
214
|
+
` [text-field-stance WARN] ${f.file}:${f.line} ${f.callee}(...) at "${f.place}" has no personal stance — mark ${VALID_PERSONAL_HINT}`,
|
|
215
|
+
);
|
|
215
216
|
console.log(" Baseline comparison skipped (--no-baseline).");
|
|
216
217
|
return { violations: [] };
|
|
217
218
|
}
|
|
@@ -67,6 +67,8 @@ function isKnownReason(r: string): r is WrapperReason {
|
|
|
67
67
|
// ── Built-in filter ─────────────────────────────────────────────────────
|
|
68
68
|
|
|
69
69
|
const BUILTIN_NAMES = new Set([
|
|
70
|
+
"test",
|
|
71
|
+
"exec",
|
|
70
72
|
"push",
|
|
71
73
|
"pop",
|
|
72
74
|
"shift",
|
|
@@ -300,9 +302,6 @@ function extractCalleeName(callNode: ReturnType<FnNode["getBody"]> | undefined):
|
|
|
300
302
|
if (!expr) return null;
|
|
301
303
|
if (expr.isKind(SyntaxKind.Identifier)) return expr.getText();
|
|
302
304
|
if (expr.isKind(SyntaxKind.PropertyAccessExpression)) {
|
|
303
|
-
// `/re/.test(x)` is a RegExp method call, not a call to a function named
|
|
304
|
-
// "test" — a regex-literal receiver is never a wrapper callee.
|
|
305
|
-
if (expr.getExpression().isKind(SyntaxKind.RegularExpressionLiteral)) return null;
|
|
306
305
|
return expr.getName();
|
|
307
306
|
}
|
|
308
307
|
return null;
|
package/src/index.ts
CHANGED
package/src/pre-push.sh
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
# scripts/check-wt.sh, delegate to it.
|
|
7
7
|
# 2. Else if running inside a worktree under the parent workspace (no
|
|
8
8
|
# check-wt.sh), run this worktree's own package.json scripts
|
|
9
|
-
# (typecheck, lint, test, test:dom)
|
|
10
|
-
#
|
|
11
|
-
#
|
|
9
|
+
# (typecheck, lint, test, test:dom) plus the consumer-CI guards
|
|
10
|
+
# (kumiko-guards guards/checks, kumiko-guard-comment-lang) directly —
|
|
11
|
+
# the parent's `bun check` would otherwise check the main checkout
|
|
12
|
+
# instead of the worktree.
|
|
12
13
|
# 3. Else if running inside the cosmicdriftgamestudio Parent-Workspace,
|
|
13
14
|
# delegate to its `bun check` with KUMIKO_CLI_SCOPE set to THIS
|
|
14
15
|
# sub-repo only — so a push from one sub-repo doesn't run
|
|
@@ -129,6 +130,24 @@ if [ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ] \
|
|
|
129
130
|
cd "$REPO_ROOT" && exec "$REPO_ROOT/scripts/check-wt.sh"
|
|
130
131
|
fi
|
|
131
132
|
|
|
133
|
+
# Runs a guard bin from the worktree's or the parent's node_modules/.bin
|
|
134
|
+
# (same guards consumer CI runs); an unresolvable bin is skipped, not fatal.
|
|
135
|
+
run_guard_bin() {
|
|
136
|
+
GUARD_LABEL="$1"
|
|
137
|
+
GUARD_BIN="$2"
|
|
138
|
+
shift 2
|
|
139
|
+
for GUARD_CANDIDATE in "$REPO_ROOT/node_modules/.bin/$GUARD_BIN" "$PARENT_DIR/node_modules/.bin/$GUARD_BIN"; do
|
|
140
|
+
if [ -x "$GUARD_CANDIDATE" ]; then
|
|
141
|
+
echo "[pre-push] $GUARD_BIN $*"
|
|
142
|
+
if ! "$GUARD_CANDIDATE" "$@"; then
|
|
143
|
+
FAILED="$FAILED $GUARD_LABEL"
|
|
144
|
+
fi
|
|
145
|
+
return 0
|
|
146
|
+
fi
|
|
147
|
+
done
|
|
148
|
+
echo "[pre-push] $GUARD_BIN: not resolvable in worktree/parent, skipped"
|
|
149
|
+
}
|
|
150
|
+
|
|
132
151
|
# infra#899: no tracked scripts/check-wt.sh — the parent-scoped `bun check`
|
|
133
152
|
# branch below would check the main checkout, not this worktree. Run the
|
|
134
153
|
# worktree's own package.json scripts directly instead (only when nested
|
|
@@ -137,7 +156,7 @@ fi
|
|
|
137
156
|
if [ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ] \
|
|
138
157
|
&& [ -n "$PARENT_DIR" ]; then
|
|
139
158
|
cd "$REPO_ROOT"
|
|
140
|
-
echo "[pre-push] worktree without scripts/check-wt.sh — running this worktree's package.json scripts (typecheck, lint, test, test:dom)…"
|
|
159
|
+
echo "[pre-push] worktree without scripts/check-wt.sh — running this worktree's package.json scripts (typecheck, lint, test, test:dom) and guards (guards, checks, comment-lang)…"
|
|
141
160
|
if ! has_package_script test; then
|
|
142
161
|
echo "[pre-push] FATAL: worktree without a package.json \"test\" script — nothing to run, refusing push." >&2
|
|
143
162
|
echo "Add a \"test\" script to package.json, or set PRE_PUSH_SKIP=1 to bypass (use sparingly)." >&2
|
|
@@ -154,6 +173,9 @@ if [ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ] \
|
|
|
154
173
|
echo "[pre-push] $SCRIPT_NAME: no script, skipped"
|
|
155
174
|
fi
|
|
156
175
|
done
|
|
176
|
+
run_guard_bin guards kumiko-guards guards
|
|
177
|
+
run_guard_bin checks kumiko-guards checks
|
|
178
|
+
run_guard_bin comment-lang kumiko-guard-comment-lang --touched --base=origin/main
|
|
157
179
|
if [ -n "$FAILED" ]; then
|
|
158
180
|
echo "[pre-push] worktree check failed:$FAILED" >&2
|
|
159
181
|
exit 1
|