Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d7240ba
test(kpi): reject compact credential-shaped source ids
seonghobae Aug 23, 2026
0f086fc
test(kpi): keep source-id expectations aligned with policy
seonghobae Aug 23, 2026
dc7fe1e
fix(kpi): reject compact credential-shaped source ids
seonghobae Aug 23, 2026
fec97a1
test(kpi): reject password-shaped source IDs
seonghobae Aug 23, 2026
72a8804
fix(kpi): reject password-shaped source IDs
seonghobae Aug 23, 2026
134508f
test(kpi): reject control characters in source IDs
seonghobae Aug 23, 2026
b0e8964
fix(kpi): reject control characters in source IDs
seonghobae Aug 23, 2026
cf90269
test(kpi): reject authorization-shaped source IDs
seonghobae Aug 23, 2026
88cdb03
fix(kpi): reject authorization-shaped source IDs
seonghobae Aug 23, 2026
6e5776c
test(kpi): reject ambiguous Unicode source IDs
seonghobae Aug 23, 2026
35bc0d0
fix(kpi): reject ambiguous Unicode source IDs
seonghobae Aug 23, 2026
e4028e5
test(kpi): reject non-canonical Unicode spaces in source IDs
seonghobae Aug 23, 2026
7472691
fix(kpi): reject non-canonical Unicode spaces in source IDs
seonghobae Aug 23, 2026
f2e086e
test(kpi): require NFC provenance source identity
seonghobae Aug 23, 2026
2f9abd3
fix(kpi): require NFC provenance source identity
seonghobae Aug 23, 2026
587c01a
test(kpi): reject compatibility-ambiguous source IDs
seonghobae Aug 23, 2026
7b2659c
fix(kpi): require compatibility-canonical source IDs
seonghobae Aug 23, 2026
9fdd8c8
test(kpi): reject strong npm token source IDs
seonghobae Aug 23, 2026
15260fd
fix(kpi): reject strong npm token source IDs
seonghobae Aug 23, 2026
ed9b532
test(kpi): include source ID validator in exact coverage
seonghobae Aug 23, 2026
6af6391
test(kpi): exercise source ID coverage branches
seonghobae Aug 23, 2026
0ae23fa
test(kpi): reject locator-shaped source IDs
seonghobae Aug 23, 2026
9994f74
fix(kpi): reject locator-shaped source IDs
seonghobae Aug 23, 2026
a3fe812
test(kpi): reject non-hierarchical locator source IDs
seonghobae Aug 23, 2026
5b4bc50
fix(kpi): reject non-hierarchical locator source IDs
seonghobae Aug 23, 2026
89ee5ab
test(kpi): reject executable locator source IDs
seonghobae Aug 23, 2026
059ff6a
fix(kpi): reject executable locator source IDs
seonghobae Aug 23, 2026
7c607d9
test(kpi): reject credential assignment source IDs
seonghobae Aug 23, 2026
b4ec2d0
fix(kpi): reject credential assignment source IDs
seonghobae Aug 23, 2026
2c148c6
test(kpi): reject password-alias source credentials
seonghobae Aug 23, 2026
1f6fd1f
fix(kpi): reject password-alias source credentials
seonghobae Aug 23, 2026
352bd94
test(kpi): reject percent-encoded source authority
seonghobae Aug 23, 2026
ec62fd6
fix(kpi): reject percent-encoded source authority
seonghobae Aug 23, 2026
4e2cadb
chore(kpi): converge current protected main
seonghobae Aug 23, 2026
f1357b0
ci: refresh current-main merge result for PR 496
seonghobae Aug 29, 2026
34d37b0
Merge f1357b02cb457d9a0d606c6207f84267526f3a93 into 9fd64b184a7df5292…
seonghobae Aug 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions scripts/lib/source-id.mjs
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
const compactCredentialLabelPattern = /(^|[^a-z0-9])(clientsecret|sessiontoken|authtoken|accesstoken|refreshtoken|accesskeyid|secretaccesskey|signingkey)([^a-z0-9]|$)/i;
const credentialHeaderAssignmentPattern = /(^|[^a-z0-9])(authorization|bearer|credential|sig|signature|pwd|passphrase)\s*[:=]/i;
const githubCredentialTokenPattern = /(^|[^a-z0-9])(github_pat_|gh[pousr]_)/i;
const npmCredentialTokenPattern = /(^|[^a-z0-9])npm_[a-z0-9]{36}([^a-z0-9]|$)/i;
const unsafeIdentityCodePointPattern = /[\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]/u;
const nonCanonicalSpacePattern = /[\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]/u;
const percentEncodedIdentityPattern = /%[0-9A-Fa-f]{2}/;
const nonHierarchicalLocatorSchemePattern = /^(?:about|data|javascript|mailto|sms|tel|vbscript):/i;

export function hasUnsafeSourceId(value) {
const rawSourceId = String(value ?? "");
const sourceId = rawSourceId.trim();
const normalized = sourceId.toLowerCase();
const camelSeparatedSourceId = sourceId.replace(/([a-z0-9])([A-Z])/g, "$1_$2");
return rawSourceId !== sourceId
|| sourceId !== sourceId.normalize("NFKC")
|| unsafeIdentityCodePointPattern.test(sourceId)
|| nonCanonicalSpacePattern.test(sourceId)
Comment on lines 10 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Behavior change lacks changelog entry

New hasUnsafeSourceId rejections are absent from the Unreleased changelog. Repository policy requires every behavior change to be recorded.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

|| percentEncodedIdentityPattern.test(sourceId)
|| normalized === "placeholder"
|| normalized === "todo"
|| normalized === "tbd"
|| normalized.startsWith("replace-with-")
|| /https?:\/\//i.test(sourceId)
|| sourceId.includes("://")
|| nonHierarchicalLocatorSchemePattern.test(sourceId)
|| sourceId.includes("?")
|| /(^|[^a-z0-9])(github_pat_|gh[pousr]_)/i.test(sourceId)
|| /(^|[^a-z0-9])(token|secret|api[_-]?key|access[_-]?key|private[_-]?key)([^a-z0-9]|$)/i.test(sourceId);
|| githubCredentialTokenPattern.test(sourceId)
|| npmCredentialTokenPattern.test(sourceId)
|| /(^|[^a-z0-9])(token|secret|password|passwd|api[_-]?key|access[_-]?key|private[_-]?key)([^a-z0-9]|$)/i.test(camelSeparatedSourceId)
|| compactCredentialLabelPattern.test(sourceId)
|| credentialHeaderAssignmentPattern.test(sourceId);
}
23 changes: 23 additions & 0 deletions test/source-id-canonical-whitespace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,33 @@ describe("KPI source-id canonical identity", () => {
expect(hasUnsafeSourceId(sourceId)).toBe(true);
});

it.each([
"cloudflare-logpush:\nnoema-production",
"cloudflare-logpush:\u0000noema-production",
"cloudflare-logpush:\u202Enoema-production",
"cloudflare-logpush:\u2028noema-production",
"cloudflare-logpush:\u2029noema-production",
"cloudflare-logpush:\uD800noema-production",
"cloudflare-logpush:\u00A0noema-production",
"cloudflare-logpush:\u202Fnoema-production",
"cloudflare-logpush:\u3000noema-production",
])("rejects embedded control, separator, format, surrogate, or non-canonical space characters in source label %j", (sourceId) => {
expect(hasUnsafeSourceId(sourceId)).toBe(true);
});

it("rejects a canonically equivalent but non-NFC source label", () => {
expect(hasUnsafeSourceId("cloudflare-logpush:noema-e\u0301")).toBe(true);
});

it("rejects a compatibility-equivalent but non-NFKC source label", () => {
expect(hasUnsafeSourceId("cloudflare-logpush:noema-production")).toBe(true);
});

it.each([
"cloudflare-logpush:noema-production",
"github-app:noema-reviewer",
"github:repository-noema",
"cloudflare-logpush:noema-é",
])("keeps exact non-secret source label %s", (sourceId) => {
expect(hasUnsafeSourceId(sourceId)).toBe(false);
});
Expand Down
42 changes: 42 additions & 0 deletions test/source-id-exact-coverage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, expect, it } from "vitest";
import { hasUnsafeSourceId } from "../scripts/lib/source-id.mjs";

describe("source-id exact production coverage", () => {
it.each([
" source",
"source ",
"source",
"source\u0000id",
"source\u202Eid",
"source\uD800id",
"source\u2028id",
"source\u2029id",
"source\u00A0id",
"placeholder",
"todo",
"tbd",
"replace-with-source",
"https://logs.noema.internal/export",
"source?token=redacted",
"ghp_EXAMPLEVALUE123456",
"npm_abcdefghijklmnopqrstuvwxyz0123456789",
"token=EXAMPLEVALUE123456",
"apiKey=EXAMPLEVALUE123456",
"clientSecret=EXAMPLEVALUE123456",
"authorization=Bearer EXAMPLEVALUE123456",
])("executes a fail-closed source identity branch for %j", (sourceId) => {
expect(hasUnsafeSourceId(sourceId)).toBe(true);
});

it.each([
undefined,
null,
"",
"cloudflare-logpush:noema-production",
"github-app:noema-reviewer",
"npm-package-metadata",
"source-é",
])("executes a non-secret source identity branch for %j", (sourceId) => {
expect(hasUnsafeSourceId(sourceId)).toBe(false);
});
});
56 changes: 55 additions & 1 deletion test/source-id-secret-prefixes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,55 @@ describe("KPI source-id credential prefix safety", () => {
"github:ghu_EXAMPLEVALUE123456",
"github:ghs_EXAMPLEVALUE123456",
"github:ghr_EXAMPLEVALUE123456",
])("rejects GitHub credential-shaped source label %s", (sourceId) => {
"npm_abcdefghijklmnopqrstuvwxyz0123456789",
])("rejects strong credential-shaped source label %s", (sourceId) => {
expect(hasUnsafeSourceId(sourceId)).toBe(true);
});

it.each([
"clientSecret=EXAMPLEVALUE123456",
"clientsecret=EXAMPLEVALUE123456",
"sessionToken=EXAMPLEVALUE123456",
"sessiontoken=EXAMPLEVALUE123456",
"accessKeyId=AKIAEXAMPLEVALUE",
"accesskeyid=AKIAEXAMPLEVALUE",
"refreshToken=EXAMPLEVALUE123456",
"refreshtoken=EXAMPLEVALUE123456",
"secretAccessKey=EXAMPLEVALUE123456",
"secretaccesskey=EXAMPLEVALUE123456",
"password=EXAMPLEVALUE123456",
"passwd=EXAMPLEVALUE123456",
"pwd=EXAMPLEVALUE123456",
"passphrase=EXAMPLEVALUE123456",
"authorization=Bearer EXAMPLEVALUE123456",
"bearer=EXAMPLEVALUE123456",
"credential=EXAMPLEVALUE123456",
"signature=EXAMPLEVALUE123456",
"sig=EXAMPLEVALUE123456",
])("rejects compact or credential-header-shaped source label %s", (sourceId) => {
expect(hasUnsafeSourceId(sourceId)).toBe(true);
});

it.each([
"%67hp_EXAMPLEVALUE123456",
"pwd%3DEXAMPLEVALUE123456",
"passphrase%253DEXAMPLEVALUE123456",
"https%3A%2F%2Flogs.example.invalid%2Fnoema",
])("rejects percent-encoded ambiguous source authority %s", (sourceId) => {
expect(hasUnsafeSourceId(sourceId)).toBe(true);
});

it.each([
"s3://noema-production-kpi",
"ftp://logs.example.invalid/noema",
"file:///var/log/noema.ndjson",
"mailto:operator@example.com",
"data:text/plain,production-log",
"tel:+12025550123",
"javascript:alert(1)",
"vbscript:msgbox(1)",
"about:blank",
])("rejects locator-shaped source label %s", (sourceId) => {
expect(hasUnsafeSourceId(sourceId)).toBe(true);
});

Expand All @@ -18,6 +66,12 @@ describe("KPI source-id credential prefix safety", () => {
"github-app:noema-reviewer",
"github:repository-noema",
"ghp-metrics",
"npm-package-metadata",
"credential-registry-production",
"signature-verifier-production",
"passwordless-auth-production",
"passphrase-policy-production",
"success-rate-95%",
])("keeps descriptive non-secret source label %s", (sourceId) => {
expect(hasUnsafeSourceId(sourceId)).toBe(false);
});
Expand Down
1 change: 1 addition & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default defineConfig({
"scripts/prepare-agent-pr-message.mjs",
"scripts/verify-orchestrator-gateway.mjs",
"scripts/lib/orchestrator-gateway.mjs",
"scripts/lib/source-id.mjs",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Local test result unavailable

npm test cannot run without installed dependencies. Exact-head CI must establish the test and 100% coverage results.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

"scripts/workflow-registry-audit.mjs",
"scripts/workflow-registry-disable-plan.mjs",
"scripts/workflow-registry-live-disable.mjs",
Expand Down
Loading