Skip to content
Merged
Show file tree
Hide file tree
Changes from 250 commits
Commits
Show all changes
569 commits
Select commit Hold shift + click to select a range
717d6bd
test(oidc): align non-object rejection contract
seonghobae Aug 27, 2026
2e1cd10
fix(oidc): remove unreachable non-object claim branch
seonghobae Aug 27, 2026
22ff97b
test(worker): reject normalized trace authority
seonghobae Aug 27, 2026
16ba120
fix(worker): preserve canonical trace authority
seonghobae Aug 27, 2026
58caf63
test(oidc): require central workflow repository authority
seonghobae Aug 27, 2026
15d495f
test(oidc): bind regression to protected worker boundary
seonghobae Aug 27, 2026
01d7c3e
fix(oidc): require central workflow repository authority
seonghobae Aug 27, 2026
b42691e
fix(readiness): bind workflow authority to central repository
seonghobae Aug 27, 2026
517b9e6
test(oidc): model configured distributed limit in trust regression
seonghobae Aug 27, 2026
3529a4e
test(oidc): cover central repository owner mismatch
seonghobae Aug 27, 2026
b8c3e84
test(github): bound installation token authority
seonghobae Aug 27, 2026
e7486e4
fix(github): bound installation token authority
seonghobae Aug 27, 2026
05fcbb2
test(observability): require collision-resistant OIDC subject receipt
seonghobae Aug 27, 2026
2d08525
fix(observability): strengthen OIDC subject evidence identity
seonghobae Aug 27, 2026
ae6bd2a
test(exchange): reject unreviewed request authority
seonghobae Aug 27, 2026
ea50fb1
fix(exchange): reject unreviewed request authority
seonghobae Aug 27, 2026
2afc8f4
docs(api): close exchange request schema
seonghobae Aug 27, 2026
9329b00
test(exchange): converge exact-schema regressions
seonghobae Aug 27, 2026
498955e
test(readiness): preserve canonical trace correlation
seonghobae Aug 27, 2026
d58b6aa
fix(readiness): preserve canonical trace correlation
seonghobae Aug 27, 2026
0530be8
test(oidc): reject ambiguous JWKS key identifiers
seonghobae Aug 28, 2026
44702a3
fix(oidc): reject ambiguous JWKS key identifiers
seonghobae Aug 28, 2026
0dd4653
test(oidc): reject unsupported critical JOSE headers
seonghobae Aug 28, 2026
71cb7f3
test(oidc): bind critical JOSE rejection to bearer boundary
seonghobae Aug 28, 2026
bc6f4d6
fix(oidc): reject unsupported critical JOSE headers
seonghobae Aug 28, 2026
ff803d5
test(oidc): reject unsupported b64 signing semantics
seonghobae Aug 28, 2026
489ba34
fix(oidc): reject unsupported b64 signing semantics
seonghobae Aug 28, 2026
20edfc0
test(rate-limit): reject normalized IPv6 client aliases
seonghobae Aug 28, 2026
0b8ccec
fix(rate-limit): reject non-canonical IPv6 client aliases
seonghobae Aug 28, 2026
ec5ff0c
fix(rate-limit): preserve semantic IPv6 bucket identity
seonghobae Aug 28, 2026
6f952dd
test(oidc): reject mismatched metadata media types
seonghobae Aug 28, 2026
e2746c3
fix(oidc): bind metadata responses to JSON media type
seonghobae Aug 28, 2026
4115783
test(oidc): cover missing JWKS media type
seonghobae Aug 28, 2026
6bdc55f
test(oidc): cover absent metadata content types
seonghobae Aug 28, 2026
8788bd7
test(github-api): reject unreviewed JSON media type
seonghobae Aug 28, 2026
8bf2ec8
fix(github-api): bind JSON responses to reviewed media type
seonghobae Aug 28, 2026
f780dc0
chore(source): restore final newline after GitHub API repair
seonghobae Aug 28, 2026
26d2066
test(github-api): cover missing JSON media type
seonghobae Aug 28, 2026
25070d4
test(github-api): reject BOM-normalized credential JSON
seonghobae Aug 28, 2026
27e8930
test(oidc): reject BOM-normalized discovery JSON
seonghobae Aug 28, 2026
e2fc03d
fix(json): reject normalized external authority bytes
seonghobae Aug 28, 2026
aa8584f
test(github-api): reject duplicate credential keys
seonghobae Aug 28, 2026
3a5c314
fix(json): reject duplicate external authority keys
seonghobae Aug 28, 2026
9303644
test(oidc): reject duplicate nested JWK authority keys
seonghobae Aug 28, 2026
c305e4c
fix(json): reject nested duplicate authority keys
seonghobae Aug 28, 2026
07c1994
test(json): cover whitespace before authority separators
seonghobae Aug 28, 2026
c7d2e63
test(json): bound streamed external authority responses
seonghobae Aug 28, 2026
39af6c4
fix(json): bound external authority response bodies
seonghobae Aug 28, 2026
3488d91
test(json): cover empty external authority body
seonghobae Aug 28, 2026
bd19ad0
test(oidc): require current central workflow source
seonghobae Aug 28, 2026
02ff991
fix(oidc): roll trusted workflow source forward
seonghobae Aug 28, 2026
9f73921
docs(architecture): bind current central workflow trust
seonghobae Aug 28, 2026
9b175ee
test(oidc): reject non-string JOSE key ids
seonghobae Aug 28, 2026
c7b581c
fix(oidc): reject non-string JOSE key ids
seonghobae Aug 28, 2026
3f053f5
test(oidc): reject stale issued-at authority
seonghobae Aug 28, 2026
d6f572f
fix(oidc): bound issued-at token lifetime
seonghobae Aug 28, 2026
0479133
test(oidc): require current audited central trust source
seonghobae Aug 28, 2026
f4f7f52
fix(oidc): roll trusted central workflow source forward
seonghobae Aug 28, 2026
ce26183
docs(architecture): bind current audited workflow source
seonghobae Aug 28, 2026
dd95e3b
test(oidc): bound JOSE key-id discovery authority
seonghobae Aug 28, 2026
067609a
fix(oidc): bound JOSE key-id discovery selector
seonghobae Aug 28, 2026
c035471
test(ci): require reusable patch-validator build cache
seonghobae Aug 28, 2026
fdf4bd4
fix(ci): cache exact-head patch-validator builds
seonghobae Aug 28, 2026
b20ca3d
test(readiness): fail closed without credential fetch capability
seonghobae Aug 28, 2026
cdc9d83
fix(readiness): require credential fetch capability
seonghobae Aug 28, 2026
8b6f932
test(readiness): reject non-installable fetch capability
seonghobae Aug 28, 2026
740e126
fix(readiness): require replaceable credential fetch capability
seonghobae Aug 28, 2026
f28d02c
test(exchange): reject overlong GitHub repository authority
seonghobae Aug 28, 2026
0cde5cb
fix(exchange): bound GitHub repository name authority
seonghobae Aug 28, 2026
a20ded2
test(openapi): bound repository locator name segment
seonghobae Aug 28, 2026
39e4ded
fix(openapi): bound repository locator name segment
seonghobae Aug 28, 2026
0da20e3
docs(api): document repository-name authority bound
seonghobae Aug 28, 2026
ed63294
docs(security): align repository-name validation contract
seonghobae Aug 28, 2026
481889f
test(egress): reject overlong repository token scope
seonghobae Aug 28, 2026
3a905c8
fix(egress): bound repository token scope
seonghobae Aug 28, 2026
3feafcf
test(openapi): bind pre-limiter 405 headers
seonghobae Aug 28, 2026
eaa4b82
fix(openapi): align pre-limiter 405 headers
seonghobae Aug 28, 2026
a5b173a
test(openapi): align distributed header authority
seonghobae Aug 28, 2026
0a37077
test(oidc): require current central workflow source
seonghobae Aug 28, 2026
9233f62
fix(oidc): trust current central workflow source
seonghobae Aug 28, 2026
62b1527
docs(architecture): record current central trust rollforward
seonghobae Aug 28, 2026
7cbfcaa
test(oidc): roll trusted central workflow source
seonghobae Aug 28, 2026
1bed960
fix(oidc): trust current protected central source
seonghobae Aug 28, 2026
fba53f1
docs(architecture): record current central workflow trust
seonghobae Aug 28, 2026
6e3fddf
test(oidc): require authoritative metadata status
seonghobae Aug 28, 2026
f9771f6
fix(oidc): require authoritative metadata status
seonghobae Aug 28, 2026
806d13b
test(github): require endpoint-specific success status authority
seonghobae Aug 28, 2026
3734c00
fix(github): bind JSON success to endpoint status
seonghobae Aug 28, 2026
c199798
test(github): align app token fixtures with HTTP 201
seonghobae Aug 28, 2026
c00ee25
test(github): use HTTP 201 token-mint fixtures
seonghobae Aug 28, 2026
5a534ae
test(github): model token creation as HTTP 201
seonghobae Aug 28, 2026
3e49c9c
test(github): model successful token creation status
seonghobae Aug 28, 2026
cde6492
test(github): preserve token expiry semantics under HTTP 201
seonghobae Aug 28, 2026
2b1377f
test(github): model expiry fixture as HTTP 201
seonghobae Aug 28, 2026
ef25107
test(github): preserve token response semantics under HTTP 201
seonghobae Aug 28, 2026
ea58ea1
test(github): model worker token responses as HTTP 201
seonghobae Aug 28, 2026
6edea7a
test(oidc): reject contradictory JWKS key operations
seonghobae Aug 28, 2026
de5b2cf
fix(oidc): enforce JWKS verification key operations
seonghobae Aug 28, 2026
f899ad9
test(egress): reject unused app installation listing authority
seonghobae Aug 28, 2026
978c54b
fix(egress): remove unused app installation listing authority
seonghobae Aug 28, 2026
f5a2875
test(egress): keep redirect coverage on used installation lookup
seonghobae Aug 28, 2026
7cb8ca8
test(egress): bind authorization framing to used lookup
seonghobae Aug 28, 2026
e9193ed
test(rate-limit): bound local client bucket cardinality
seonghobae Aug 29, 2026
eb8c721
fix(rate-limit): bound local client bucket retention
seonghobae Aug 29, 2026
0c65c58
fix(rate-limit): restore canonical source newline
seonghobae Aug 29, 2026
bfb6f88
test(rate-limit): cover expired bucket cleanup at capacity
seonghobae Aug 29, 2026
33b6c1d
fix(rate-limit): remove unreachable capacity branch
seonghobae Aug 29, 2026
42fcb19
test(rate-limit): preserve renewed bucket eviction recency
seonghobae Aug 29, 2026
e2095a8
fix(rate-limit): refresh renewed bucket eviction recency
seonghobae Aug 29, 2026
9a62f08
test(ci): preserve in-flight validator cache seed
seonghobae Aug 29, 2026
e111bf3
fix(ci): let validator builds seed shared cache
seonghobae Aug 29, 2026
b8a9254
test(exchange): reject misleading JSON media types in base worker
seonghobae Aug 29, 2026
66982b4
fix(exchange): enforce reviewed JSON media type in base worker
seonghobae Aug 29, 2026
3a22b65
test(exchange): align helper coverage with reviewed media type
seonghobae Aug 29, 2026
07e0971
test(exchange): cover missing media type with request body
seonghobae Aug 29, 2026
91e7295
test(exchange): exercise truly missing media type
seonghobae Aug 29, 2026
895cd67
test(exchange): accept GitHub App PKCS#1 private key
seonghobae Aug 29, 2026
51df024
fix(exchange): normalize GitHub App PKCS#1 private keys
seonghobae Aug 29, 2026
dba837b
fix(runtime): accept GitHub App PKCS#1 private keys
seonghobae Aug 29, 2026
53206e0
test(runtime): verify GitHub App PKCS#1 readiness
seonghobae Aug 29, 2026
89580bb
fix(runtime): remove unreachable private-key branches
seonghobae Aug 29, 2026
48d0f40
test(runtime): cover private-key envelope rejection paths
seonghobae Aug 29, 2026
c12dc70
test(runtime): accept canonical PKCS8 terminal newline
seonghobae Aug 29, 2026
de79d57
fix(runtime): canonicalize PKCS8 terminal newline
seonghobae Aug 29, 2026
9900a6c
test(runtime): preserve private-key readiness cache after normalization
seonghobae Aug 29, 2026
5abf296
fix(runtime): preserve readiness cache across key normalization
seonghobae Aug 29, 2026
106dcb9
fix(runtime): type normalized credential cache source safely
seonghobae Aug 29, 2026
5aba3b5
test(runtime): reject stale cached binding authority
seonghobae Aug 29, 2026
14dcc58
fix(runtime): drop withdrawn cached bindings
seonghobae Aug 29, 2026
6324905
test(oidc): reject encryption-use JWKS keys
seonghobae Aug 29, 2026
8045096
test(trust): require current central workflow source
seonghobae Aug 29, 2026
efad421
fix(oidc): honor JWKS signing-use authority
seonghobae Aug 29, 2026
311c76e
fix(trust): roll forward audited central source
seonghobae Aug 29, 2026
a878de0
docs(architecture): record audited central trust roll-forward
seonghobae Aug 29, 2026
eb28212
test(trust): require latest central workflow source
seonghobae Aug 29, 2026
d307e4a
fix(trust): follow current protected central source
seonghobae Aug 29, 2026
6fe3e76
docs(architecture): track latest central trust authority
seonghobae Aug 29, 2026
0d4dc6f
test(egress): reject unreviewed anonymous GitHub API calls
seonghobae Aug 29, 2026
5e4c0f8
test(egress): preserve exact public metadata diagnostic
seonghobae Aug 29, 2026
237334a
chore(test): drop superseded egress experiment
seonghobae Aug 29, 2026
4e8b6f3
test(private-key): reject bare-CR PEM termination
seonghobae Aug 29, 2026
3d740e6
fix(private-key): reject bare-CR PEM termination
seonghobae Aug 29, 2026
ddc1df8
test(exchange): reject unreviewed query authority
seonghobae Aug 29, 2026
e504385
fix(exchange): reject unreviewed query authority
seonghobae Aug 29, 2026
8a30904
test(exchange): reject bare query delimiter authority
seonghobae Aug 29, 2026
5bc743a
fix(exchange): require exact resource-form URL
seonghobae Aug 29, 2026
609af78
fix(exchange): preserve stable validation message
seonghobae Aug 29, 2026
2a6a922
test(github-app): refresh stale installation cache
seonghobae Aug 29, 2026
b39b4f1
fix(github-app): refresh retired cached installation ids
seonghobae Aug 29, 2026
d3ea853
test(github-app): retry installation refresh only from cache
seonghobae Aug 29, 2026
484f39c
fix(github-app): refresh only cached installation authority
seonghobae Aug 29, 2026
bfae9ee
ci: retrigger exact-head patch validator
seonghobae Aug 29, 2026
3d2c4d3
test(egress): restrict anonymous GitHub API authority
seonghobae Aug 29, 2026
f2e0802
fix(egress): bound anonymous GitHub API authority
seonghobae Aug 29, 2026
9cce753
merge(main): adopt protected #505 integration for egress repair
seonghobae Aug 29, 2026
05e4a66
merge(main): retain licensing integration with egress repair
seonghobae Aug 29, 2026
cba0900
test(egress): align legacy cases with reviewed operations
seonghobae Aug 29, 2026
dc6c302
test(egress): reject unreviewed outbound header authority
seonghobae Aug 29, 2026
b2367eb
fix(egress): bound outbound header authority
seonghobae Aug 29, 2026
bbb4e98
test(egress): align retired listing destination verdict
seonghobae Aug 29, 2026
6ee5540
test(egress): use reviewed GitHub media type
seonghobae Aug 29, 2026
77c6b39
test(egress): match production token-mint headers
seonghobae Aug 29, 2026
75e6e1e
test(egress): require exact token-mint JSON media type
seonghobae Aug 29, 2026
fa11c84
fix(egress): bind token mint to JSON media type
seonghobae Aug 29, 2026
a3046ad
fix(egress): canonicalize token mint media type
seonghobae Aug 29, 2026
ac96ea4
test(egress): bind scope cases to JSON media type
seonghobae Aug 29, 2026
8d5f0c0
test(egress): bind token operation to JSON media type
seonghobae Aug 29, 2026
b802dc5
test(egress): prove canonical token media type on wire
seonghobae Aug 29, 2026
d94a069
test(egress): close token media-type branch coverage
seonghobae Aug 29, 2026
a4a318b
test(egress): cover noncanonical reviewed header value
seonghobae Aug 29, 2026
b4812ae
test(egress): cover rejected-header short circuit
seonghobae Aug 29, 2026
f4527e9
test(egress): prove reviewed production header contract
seonghobae Aug 29, 2026
cf3a756
test(github-app): bind configured installation to repository
seonghobae Aug 29, 2026
e87212d
fix(github-app): verify configured installation ownership
seonghobae Aug 29, 2026
71859c3
test(github-app): reflect repository-bound configured installs
seonghobae Aug 29, 2026
355ca3d
test(egress): require canonical Bearer framing
seonghobae Aug 29, 2026
cb7cf4d
fix(egress): enforce canonical Bearer scheme
seonghobae Aug 29, 2026
69cde35
test(egress): reject non-canonical HTTP method casing
seonghobae Aug 29, 2026
172cc90
fix(egress): preserve canonical HTTP method bytes
seonghobae Aug 29, 2026
39d9b63
test(egress): fail closed on malformed request headers
seonghobae Aug 29, 2026
0239b29
fix(egress): fail closed on malformed headers
seonghobae Aug 29, 2026
ab15f39
test(egress): require explicit installation token media authority
seonghobae Aug 29, 2026
b5eb23f
test(egress): preserve canonical media-type synthesis contract
seonghobae Aug 29, 2026
07c9b13
test(egress): fail closed on malformed cancellation authority
seonghobae Aug 29, 2026
5a550d5
fix(egress): fail closed on invalid cancellation signals
seonghobae Aug 29, 2026
e135ad9
test(egress): classify streamed deadline expiry as timeout
seonghobae Aug 29, 2026
c19549b
fix(egress): preserve streamed timeout authority
seonghobae Aug 29, 2026
2c065df
test(egress): reject pre-aborted caller authority before transport
seonghobae Aug 29, 2026
7615cfc
fix(egress): honor pre-aborted caller signal before transport
seonghobae Aug 29, 2026
6e71cbf
test(ci): require stale image runs to cancel
seonghobae Aug 29, 2026
8819ec6
fix(ci): cancel superseded image verification runs
seonghobae Aug 29, 2026
0c3f1f3
test(ci): require resilient image scanner downloads
seonghobae Aug 29, 2026
bd6e936
fix(ci): retry transient scanner release downloads
seonghobae Aug 29, 2026
1208839
test(egress): preserve mid-flight caller cancellation
seonghobae Aug 29, 2026
804f6ed
fix(egress): honor mid-flight caller cancellation
seonghobae Aug 29, 2026
ffa11e6
test(egress): cover bodyless late cancellation
seonghobae Aug 29, 2026
2828948
test(egress): preserve cancellation during body streaming
seonghobae Aug 29, 2026
439fa8f
fix(egress): honor cancellation during body streaming
seonghobae Aug 29, 2026
873496d
test(egress): preserve cancellation over late transport rejection
seonghobae Aug 29, 2026
3677562
fix(egress): preserve caller abort reason on late rejection
seonghobae Aug 29, 2026
2eb620d
test(egress): reject normalized reviewed header authority
seonghobae Aug 29, 2026
36bfd2c
fix(egress): preserve raw reviewed header authority
seonghobae Aug 29, 2026
0212ef8
test(egress): require timeout despite abort-ignoring body
seonghobae Aug 29, 2026
00c6161
fix(egress): enforce abort-aware response reads
seonghobae Aug 29, 2026
271d47b
fix(egress): type abort listener for exact deadline
seonghobae Aug 29, 2026
6faee9f
test(egress): exercise active abort race
seonghobae Aug 29, 2026
5f6ea1a
test(egress): cover post-read cancellation race
seonghobae Aug 29, 2026
dc3fb50
fix(egress): remove synthetic abort no-op
seonghobae Aug 29, 2026
2d79b35
test(ci): require license evidence before manifest
seonghobae Aug 29, 2026
44107bd
fix(ci): materialize license evidence before manifest
seonghobae Aug 29, 2026
3b4abd3
test(egress): require abort-bounded transport headers
seonghobae Aug 29, 2026
0f75d39
test(egress): cover late transport cleanup branches
seonghobae Aug 29, 2026
7726c93
fix(egress): bound transport wait to abort authority
seonghobae Aug 29, 2026
1be5bd4
test(egress): cover synchronous abort transport cleanup
seonghobae Aug 29, 2026
1cb9a10
fix(egress): clean late transport on synchronous abort
seonghobae Aug 29, 2026
32046d1
test(egress): await caller cancellation deterministically
seonghobae Aug 29, 2026
acf9659
test(egress): cover response-settle cancellation race
seonghobae Aug 29, 2026
84bc542
test(egress): deterministically cover post-transport revocation
seonghobae Aug 29, 2026
a76c1d4
test(ci): require branch coverage failure diagnostics
seonghobae Aug 29, 2026
f307d13
fix(ci): report uncovered branches on coverage failure
seonghobae Aug 29, 2026
9a5d86a
test(acquisition): align CLI audit timeout hierarchy
seonghobae Aug 29, 2026
d47073d
test(egress): cover native transport-settlement revocation race
seonghobae Aug 29, 2026
6c8d90b
test(egress): cover revoked bodyless transport response
seonghobae Aug 29, 2026
e230074
test(ci): require concrete branch diagnostic locations
seonghobae Aug 29, 2026
df110e8
fix(ci): emit concrete branch coverage locations
seonghobae Aug 29, 2026
ed707c7
test(image): require patched OpenSSL security floor
seonghobae Aug 30, 2026
41191c1
fix(image): link static Node against OpenSSL 3.5.8
seonghobae Aug 30, 2026
784d37e
test(image): anchor Node PATH regression to Node build
seonghobae Aug 30, 2026
d4a895e
test(image): require pre-materialized validator dependencies
seonghobae Aug 30, 2026
417eabc
fix(image): pre-materialize validator dependencies
seonghobae Aug 30, 2026
83d21b8
fix(image): use local validator dependency context
seonghobae Aug 30, 2026
db11e1c
test(image): align contract with bounded dependency context
seonghobae Aug 30, 2026
cc20967
docs(governance): align security scan authority
seonghobae Aug 30, 2026
bee00f8
test(governance): align central security-scan trigger contract
seonghobae Aug 30, 2026
59f0728
test(image): require runner-pinned runtime sources
seonghobae Aug 30, 2026
25adc41
test(image): require bounded runtime source fetches
seonghobae Aug 30, 2026
ca2155e
fix(image): bound checksum-pinned source fetches
seonghobae Aug 30, 2026
7dfacbf
test(image): align bounded source-fetch contract
seonghobae Aug 30, 2026
971989a
test(image): require bounded scanner downloads
seonghobae Aug 30, 2026
217b336
fix(image): bound scanner asset downloads
seonghobae Aug 30, 2026
5b3cd3d
fix(image): constrain source redirect protocols
seonghobae Aug 30, 2026
d675e54
fix(image): restore exact verification contract
seonghobae Aug 30, 2026
68b0e43
fix(image): bound scanner asset transport
seonghobae Aug 30, 2026
d51ef8a
test(image): align scanner retry bound with hardened workflow
seonghobae Aug 30, 2026
4599144
Merge d51ef8ac0cf87fda183c7680c2f2b017e496d10b into 9fd64b184a7df5292…
seonghobae Aug 30, 2026
7b8f109
test(egress): reject headers on anonymous meta
seonghobae Aug 30, 2026
226553d
test(egress): prove body cancellation is immediate
seonghobae Aug 30, 2026
20aaafa
test(supply-chain): bind Node source URL to digest
seonghobae Aug 30, 2026
ef0d57e
fix(egress): keep anonymous meta headerless
seonghobae Aug 30, 2026
738aa68
chore(stack): converge #508 onto protected #500 merge
seonghobae Aug 30, 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
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,25 @@ jobs:
diagnosticCount += 1;
if (diagnosticCount >= 64) break outer;
}
const branchMap = coverage?.branchMap ?? {};
for (const [branchId, hits] of Object.entries(coverage?.b ?? {})) {
if (!Array.isArray(hits)) continue;
const branch = branchMap[branchId];
const locations = branch?.locations ?? [];
for (const [armIndex, armHits] of hits.entries()) {
if (armHits !== 0) continue;
const location = locations[armIndex] ?? branch?.loc;
const line = branch?.line ?? branch?.loc?.start?.line ?? location?.line ?? location?.start?.line;
if (!Number.isInteger(line)) continue;
const column = branch?.loc?.start?.column ?? location?.start?.column ?? 0;
const path = relative(process.cwd(), file) || file;
console.error(
`Uncovered branch ${path}:${line}:${column} branch=${branchId} arm=${armIndex}`,
);
diagnosticCount += 1;
if (diagnosticCount >= 64) break outer;
}
}
}
NODE
fi
Expand All @@ -194,6 +213,9 @@ jobs:
- name: release KPI verification
run: npm run kpi:verify

- name: release dependency license inventory
run: npm run release:dependency-license-inventory

- name: release acquisition manifest
run: npm run acquisition:manifest

Expand Down
69 changes: 64 additions & 5 deletions .github/workflows/patch-validator-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:

concurrency:
group: noema-patch-validator-image-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false
cancel-in-progress: true

permissions:
contents: read
Expand Down Expand Up @@ -80,6 +80,20 @@ jobs:
scanner_dir="$RUNNER_TEMP/noema-binary-scanners"
mkdir -p "$scanner_dir"

download_scanner_asset() {
destination="$1"
url="$2"
timeout --signal=TERM --kill-after=30s 5m \
curl --proto '=https' --proto-redir '=https' --tlsv1.2 --location --fail --silent --show-error \
--retry 3 \
--retry-all-errors \
--retry-delay 2 \
--retry-max-time 90 \
--connect-timeout 20 \
--max-time 180 \
--output "$destination" "$url"
}

install_scanner() {
scanner="$1"
version="$2"
Expand All @@ -88,12 +102,10 @@ jobs:
checksums="${scanner}_${version}_checksums.txt"
release_base="https://github.com/anchore/${scanner}/releases/download/v${version}"

curl --proto '=https' --tlsv1.2 --location --fail --silent --show-error \
--output "$scanner_dir/$checksums" "$release_base/$checksums"
download_scanner_asset "$scanner_dir/$checksums" "$release_base/$checksums"
printf '%s %s\n' "$checksums_sha256" "$scanner_dir/$checksums" | sha256sum --check --strict

curl --proto '=https' --tlsv1.2 --location --fail --silent --show-error \
--output "$scanner_dir/$archive" "$release_base/$archive"
download_scanner_asset "$scanner_dir/$archive" "$release_base/$archive"
(
cd "$scanner_dir"
grep -E "^[0-9a-f]{64} ${archive}$" "$checksums" | sha256sum --check --strict
Expand All @@ -108,17 +120,64 @@ jobs:
"$scanner_dir/grype" version | grep -Fq "$GRYPE_VERSION"
printf 'SCANNER_BIN_DIR=%s\n' "$scanner_dir" >>"$GITHUB_ENV"

- name: Set up exact dependency materialization toolchain
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "24.19.0"
cache: npm

- name: Materialize exact patch-validator dependencies
shell: bash
run: |
set -euo pipefail
test "$(node --version)" = "v24.19.0"
test "$(npm --version)" = "11.17.0"

deps_dir="$RUNNER_TEMP/patch-validator-deps"
rm -rf "$deps_dir"
mkdir -p "$deps_dir"
cp package.json package-lock.json "$deps_dir/"
source_lock_sha="$(sha256sum package-lock.json | cut -d' ' -f1)"
copied_lock_sha="$(sha256sum "$deps_dir/package-lock.json" | cut -d' ' -f1)"
test "$copied_lock_sha" = "$source_lock_sha"

(
cd "$deps_dir"
timeout --signal=TERM --kill-after=30s 10m env \
npm_config_os=wasip1-threads \
npm_config_cpu=wasm32 \
npm ci --include=optional --ignore-scripts --no-audit --no-fund
npm pkg delete devDependencies.@cloudflare/workers-types devDependencies.wrangler
timeout --signal=TERM --kill-after=30s 5m env \
npm_config_os=wasip1-threads \
npm_config_cpu=wasm32 \
npm prune --include=optional --ignore-scripts --no-audit --no-fund
test -f node_modules/typescript/bin/tsc
test -f node_modules/vitest/vitest.mjs
test -f node_modules/@vitest/coverage-v8/package.json
test -f node_modules/@rolldown/binding-wasm32-wasi/package.json
test -z "$(find node_modules -type f -name '*.node' -print -quit)"
test ! -e node_modules/@cloudflare/workers-types
test ! -e node_modules/wrangler
test ! -e node_modules/workerd
test ! -e node_modules/miniflare
)

printf 'VALIDATOR_DEPS_CONTEXT=%s\n' "$deps_dir" >>"$GITHUB_ENV"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0

- name: Build exact-head patch-validator image
shell: bash
run: |
set -euo pipefail
test -d "$VALIDATOR_DEPS_CONTEXT/node_modules"
timeout --signal=TERM --kill-after=30s 150m docker buildx build \
--load \
--cache-from=type=gha,scope=noema-patch-validator-image \
--cache-to=type=gha,mode=max,scope=noema-patch-validator-image \
--build-context "validator_deps=${VALIDATOR_DEPS_CONTEXT}" \
--platform=linux/amd64 \
--file=Dockerfile.patch-validator \
--build-arg=SOURCE_REVISION=${SOURCE_SHA} \
Expand Down
29 changes: 16 additions & 13 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,28 @@ Worker (npm + `wrangler.toml`); tests run under Vitest.
## Agent guidance (CWL governance)

### Security & review gate
- Every PR that is eligible for the central **Security Scan** must pass that required gate. It runs
- Every PR that is expected to receive the central **Security Scan** must pass that required gate. It runs
`osv-scan` + `dependency-review` (diff-scoped) and `trivy-fs` (repo-wide,
fixable `MEDIUM/HIGH/CRITICAL`). The central workflow currently selects pull requests whose base branch is `main`, `master`, or `develop`.
A feature-base stacked PR can therefore have no Security Scan run; absence is non-passing evidence
rather than scanner success. Keep the stack in dependency order, then after its predecessor integrates
refresh or retarget the PR onto an eligible protected base and require a fresh terminal-success Security Scan
on the unchanged exact head before merge.
fixable `MEDIUM/HIGH/CRITICAL`). The current protected central workflow has no
pull-request base-branch filter, so stacked feature-base PRs are expected to
receive the same scanner workflow rather than being exempt by branch name.
An absent, queued, skipped, cancelled, stale, or failed run is non-passing
evidence rather than scanner success. Keep stacks in dependency order and
require a fresh terminal-success Security Scan on the unchanged exact head
before merge; if an expected run is absent, investigate routing instead of
treating the absence as an eligible-base exception.
- A failing **`trivy-fs` is a REAL finding, not a flake.** Read the job log — it
prints each finding's rule id / severity / file — or the run's SARIF results,
then **remediate**:
- For this repo, findings are almost always vulnerable npm dependencies: bump
the package in `package.json` and refresh `package-lock.json`
(`npm update <pkg>` or `npm install <pkg>@<fixed>`), preferring the transitive
fix. There is no Dockerfile or k8s manifest today; if you add one, `trivy-fs`
will also flag image/IaC misconfig — fix it at the source.
- Vulnerable npm dependencies belong in `package.json`/`package-lock.json`;
refresh the lockfile with the smallest compatible fixed dependency.
Dockerfile/IaC findings, including the patch-validator image definition,
must be fixed at the source rather than hidden behind scanner changes.
- Only for a genuine false positive, add a narrow, **documented**
`.trivyignore` (or `.trivyignore.yaml`) entry. Never weaken or disable the gate.
- A local scan with a stale DB misses findings. Run `trivy --download-db-only`
first, then scan the **merge ref**, not just the PR head.
- A local scan with a stale DB misses findings. Refresh scanner data before
local diagnosis and keep local evidence separate from the required central
exact-head run; local success never substitutes for the protected workflow.
- The org `code_scanning` ruleset is intentionally **CodeQL-only** (multiple
code-scanning tools can't converge on one PR ref). Gating is by the Security
Scan **job result**, not the `code_scanning` rule — do **not** add tools to
Expand Down
87 changes: 61 additions & 26 deletions Dockerfile.patch-validator
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,69 @@ FROM alpine:3.24.1@sha256:79ff19e9084a00eece421b2523fb93e22d730e2c0e525905de047e

ARG NODE_VERSION=24.19.0
ARG NODE_SOURCE_SHA256=f6d95e10a0431ee1067fc6aabe9f762908b4716dd35324e1ddb4b1466b76659f
ARG OPENSSL_VERSION=3.5.8
ARG OPENSSL_SOURCE_SHA256=a8f84a39918ec6415ce765d9b429d313ba97b8143169c172e734b9514464f5b2

RUN apk add --no-cache \
binutils-gold \
coreutils \
curl \
g++ \
gcc \
libgcc \
libgcc-static \
linux-headers \
make \
perl \
python3 \
py3-setuptools \
xz

ADD --checksum=sha256:${NODE_SOURCE_SHA256} https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}.tar.xz /tmp/node.tar.xz

RUN mkdir -p /usr/src/node \
&& tar -xJf /tmp/node.tar.xz --strip-components=1 -C /usr/src/node
RUN set -eu; \
download_exact() { \
url="$1"; \
expected="$2"; \
output="$3"; \
timeout --signal=TERM --kill-after=30s 5m \
curl --fail --location --proto '=https' --proto-redir '=https' --tlsv1.2 \
--retry 3 \
--retry-all-errors \
--retry-delay 2 \
--retry-max-time 90 \
--connect-timeout 20 \
--max-time 180 \
--output "$output" \
"$url"; \
printf '%s %s\n' "$expected" "$output" | sha256sum --check --strict; \
}; \
download_exact \
"https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}.tar.xz" \
"$NODE_SOURCE_SHA256" \
/tmp/node.tar.xz; \
download_exact \
"https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz" \
"$OPENSSL_SOURCE_SHA256" \
/tmp/openssl.tar.gz

RUN mkdir -p /usr/src/node /usr/src/openssl \
&& tar -xJf /tmp/node.tar.xz --strip-components=1 -C /usr/src/node \
&& tar -xzf /tmp/openssl.tar.gz --strip-components=1 -C /usr/src/openssl

WORKDIR /usr/src/openssl

RUN ./Configure linux-x86_64 \
no-shared \
no-tests \
--prefix=/opt/openssl \
--libdir=lib \
&& make -j"$(getconf _NPROCESSORS_ONLN)" \
&& make install_sw \
&& test -f /opt/openssl/include/openssl/opensslv.h \
&& test -f /opt/openssl/lib/libcrypto.a \
&& test -f /opt/openssl/lib/libssl.a \
&& grep -Fq "OPENSSL_VERSION_MAJOR 3" /opt/openssl/include/openssl/opensslv.h \
&& grep -Fq "OPENSSL_VERSION_MINOR 5" /opt/openssl/include/openssl/opensslv.h \
&& grep -Fq "OPENSSL_VERSION_PATCH 8" /opt/openssl/include/openssl/opensslv.h

ENV PATH="/opt/node/bin:${PATH}"
WORKDIR /usr/src/node
Expand All @@ -31,10 +77,14 @@ RUN ./configure \
--with-intl=small-icu \
--without-corepack \
--disable-single-executable-application \
--shared-openssl \
--shared-openssl-includes=/opt/openssl/include \
--shared-openssl-libpath=/opt/openssl/lib \
&& make -j"$(getconf _NPROCESSORS_ONLN)" V= \
&& make install \
&& test "$(/opt/node/bin/node --version)" = "v${NODE_VERSION}" \
&& test "$(/opt/node/bin/npm --version)" = "11.17.0" \
&& OPENSSL_VERSION="${OPENSSL_VERSION}" /opt/node/bin/node --input-type=module --eval='if (process.versions.openssl !== process.env.OPENSSL_VERSION) throw new Error(`unexpected OpenSSL ${process.versions.openssl}`)' \
&& /opt/node/bin/node --input-type=module --eval='/\p{ID_Continue}/u.test("a")' \
&& ! readelf -l /opt/node/bin/node | grep -q 'Requesting program interpreter' \
&& ! readelf -d /opt/node/bin/node | grep -q '(NEEDED)' \
Expand All @@ -49,30 +99,11 @@ RUN ./configure \
&& readelf -p .note.package /opt/node/bin/node \
| grep -Fq 'cpe:2.3:a:nodejs:node.js:24.19.0:*:*:*:*:*:*:*' \
&& test "$(/opt/node/bin/node --version)" = "v${NODE_VERSION}" \
&& OPENSSL_VERSION="${OPENSSL_VERSION}" /opt/node/bin/node --input-type=module --eval='if (process.versions.openssl !== process.env.OPENSSL_VERSION) throw new Error(`unexpected OpenSSL ${process.versions.openssl}`)' \
&& /opt/node/bin/node --input-type=module --eval='/\p{ID_Continue}/u.test("a")' \
&& ! readelf -l /opt/node/bin/node | grep -q 'Requesting program interpreter' \
&& ! readelf -d /opt/node/bin/node | grep -q '(NEEDED)'

FROM node_builder AS dependencies

WORKDIR /build

COPY package.json package-lock.json ./
RUN npm_config_os=wasip1-threads npm_config_cpu=wasm32 \
npm ci --include=optional --ignore-scripts --no-audit --no-fund \
&& npm pkg delete devDependencies.@cloudflare/workers-types devDependencies.wrangler \
&& npm_config_os=wasip1-threads npm_config_cpu=wasm32 \
npm prune --include=optional --ignore-scripts --no-audit --no-fund \
&& test -f node_modules/typescript/bin/tsc \
&& test -f node_modules/vitest/vitest.mjs \
&& test -f node_modules/@vitest/coverage-v8/package.json \
&& test -f node_modules/@rolldown/binding-wasm32-wasi/package.json \
&& test -z "$(find node_modules -type f -name '*.node' -print -quit)" \
&& test ! -e node_modules/@cloudflare/workers-types \
&& test ! -e node_modules/wrangler \
&& test ! -e node_modules/workerd \
&& test ! -e node_modules/miniflare

FROM scratch AS runtime

ARG SOURCE_REVISION
Expand All @@ -89,11 +120,15 @@ WORKDIR /workspace

COPY --from=node_builder --chown=65532:65532 /opt/node/bin/node /nodejs/bin/node
COPY --from=node_builder --chown=65532:65532 --chmod=0444 /usr/src/node/LICENSE /licenses/node/LICENSE
COPY --from=dependencies --chown=65532:65532 /build/node_modules /opt/noema/node_modules
# validator_deps is a local named BuildKit context materialized from this exact
# head's lockfile by the workflow. Copying directly from the named context avoids
# an ambiguous unpinned FROM reference while keeping npm registry resolution out
# of the Docker build authority.
COPY --from=validator_deps --chown=65532:65532 /node_modules /opt/noema/node_modules
COPY --chown=65532:65532 patch-validator/entrypoint.mjs /opt/noema/entrypoint.mjs
COPY --chown=65532:65532 patch-validator/validate-patch.mjs /opt/noema/validate-patch.mjs
COPY --chown=65532:65532 patch-validator/runtime.mjs /opt/noema/runtime.mjs
COPY --chown=65532:65532 patch-validator/validator-tsconfig.json /opt/noema/validator-tsconfig.json
COPY --chown=65532:65532 patch-validator/validator-vitest.config.mjs /opt/noema/validator-vitest.config.mjs

ENTRYPOINT ["/nodejs/bin/node", "--input-type=module", "--eval", "import { runCli } from '/opt/noema/runtime.mjs'; import { runEntrypoint } from '/opt/noema/entrypoint.mjs'; process.exitCode = runEntrypoint({ runCliImpl: runCli, writeDiagnostic: (message) => process.stderr.write(message) });"]
ENTRYPOINT ["/nodejs/bin/node", "--input-type=module", "--eval", "import { runCli } from '/opt/noema/runtime.mjs'; import { runEntrypoint } from '/opt/noema/entrypoint.mjs'; process.exitCode = runEntrypoint({ runCliImpl: runCli, writeDiagnostic: (message) => process.stderr.write(message) });"]
27 changes: 19 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -815,20 +815,19 @@ async function resolveInstallationId(
repository: string,
env: Env,
): Promise<InstallationIdResolution> {
if (env.GITHUB_APP_INSTALLATION_ID) {
const configuredInstallationId = env.GITHUB_APP_INSTALLATION_ID;
const configuredInstallationId = env.GITHUB_APP_INSTALLATION_ID;
if (configuredInstallationId) {
if (!/^[1-9]\d*$/.test(configuredInstallationId)) {
throw new ApiError("ERR_GITHUB_INSTALLATION", 500, "GitHub App installation id configuration is invalid");
}
const numericInstallationId = Number(configuredInstallationId);
if (!Number.isSafeInteger(numericInstallationId) || String(numericInstallationId) !== configuredInstallationId) {
throw new ApiError("ERR_GITHUB_INSTALLATION", 500, "GitHub App installation id configuration is invalid");
}
return { value: configuredInstallationId, source: "configured" };
}
const now = Date.now();
const cacheKey = `${env.GITHUB_API_BASE}:${env.GITHUB_APP_ID}:${repository}`;
const cached = installationIdCache.get(cacheKey);
const cached = configuredInstallationId ? undefined : installationIdCache.get(cacheKey);
if (cached && cached.expiresAtMs > now) {
return { value: cached.value, source: "cache" };
}
Expand All @@ -850,11 +849,23 @@ async function resolveInstallationId(
throw new ApiError("ERR_GITHUB_API", 502, "GitHub API returned invalid installation response");
}
const installationId = String(installation.id);
installationIdCache.set(cacheKey, {
if (configuredInstallationId && installationId !== configuredInstallationId) {
throw new ApiError(
"ERR_GITHUB_INSTALLATION",
500,
"GitHub App installation id does not match target repository",
);
}
if (!configuredInstallationId) {
installationIdCache.set(cacheKey, {
value: installationId,
expiresAtMs: now + configuredTtlMs(env.NOEMA_INSTALLATION_CACHE_TTL_SECONDS, 600, 3600),
});
}
return {
value: installationId,
expiresAtMs: now + configuredTtlMs(env.NOEMA_INSTALLATION_CACHE_TTL_SECONDS, 600, 3600),
});
return { value: installationId, source: "discovery" };
source: configuredInstallationId ? "configured" : "discovery",
};
}

async function createInstallationToken(repository: string, env: Env): Promise<InstallationToken> {
Expand Down
Loading
Loading