Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
160 commits
Select commit Hold shift + click to select a range
1270be5
test(security): reject unsafe outbound webhook destinations
seonghobae Aug 18, 2026
006cfab
test(security): register webhook destination regression
seonghobae Aug 18, 2026
9ca1417
fix(security): pin outbound webhook destinations
seonghobae Aug 18, 2026
835a220
test(security): cover special-use webhook destinations
seonghobae Aug 18, 2026
a40bc5e
docs(security): record webhook SSRF transport evidence
seonghobae Aug 18, 2026
6c53673
test(security): preserve facade integration evidence
seonghobae Aug 18, 2026
9728722
docs(changelog): record outbound webhook SSRF hardening
seonghobae Aug 18, 2026
aa0a18b
test(security): cover reserved webhook address space
seonghobae Aug 18, 2026
d4f4074
fix(security): restrict webhooks to public unicast space
seonghobae Aug 18, 2026
81093fb
test(security): preserve webhook authorization ordering
seonghobae Aug 18, 2026
f1a7159
fix(security): preserve webhook authorization before policy
seonghobae Aug 18, 2026
e7e18c6
test(security): require canonical webhook persistence
seonghobae Aug 18, 2026
be1fff9
fix(security): persist canonical webhook destinations
seonghobae Aug 18, 2026
ee3fe39
test(a11y): verify facade static asset behavior
seonghobae Aug 18, 2026
287a07b
test(a11y): configure facade fixture authentication
seonghobae Aug 18, 2026
2ceaa26
test(security): specify current review regressions
seonghobae Aug 18, 2026
614b84e
test(security): execute review regressions
seonghobae Aug 18, 2026
6d35afb
test(security): refine review regression boundary
seonghobae Aug 18, 2026
8de2331
fix(api): harden request and identity boundaries
seonghobae Aug 18, 2026
5f56325
refactor(api): remove dead header helper
seonghobae Aug 18, 2026
bdec0c9
test(webhook): require fallback across public DNS candidates
seonghobae Aug 18, 2026
d5b9fce
fix(webhook): fall back across validated addresses
seonghobae Aug 18, 2026
f25874c
test(webhook): bound candidate fallback behavior
seonghobae Aug 18, 2026
bea73f8
test(auth): require bounded OIDC token exchange
seonghobae Aug 18, 2026
33aff6a
test(auth): execute OIDC timeout regression
seonghobae Aug 18, 2026
e856a6d
fix(auth): bound OIDC token exchange
seonghobae Aug 18, 2026
2b1ddf9
test(security): reproduce inherited static-map lookup
seonghobae Aug 18, 2026
a634de8
test(security): execute static prototype-pollution regression
seonghobae Aug 18, 2026
afaffbd
fix(security): make static allowlist prototype-free
seonghobae Aug 18, 2026
ec54783
fix(regression): preserve logout-all token email
seonghobae Aug 18, 2026
5396c7a
test(fetch): reproduce consumed Request fallback
seonghobae Aug 18, 2026
9620ade
fix(fetch): forward the effective Request
seonghobae Aug 18, 2026
9d6cd27
test(oidc): require signed issuer-bound nonce tokens
seonghobae Aug 18, 2026
235883e
fix(oidc): bind and verify ID token claims
seonghobae Aug 18, 2026
e7d8d4d
test(oidc): reject provider redirects
seonghobae Aug 18, 2026
e886054
fix(oidc): reject provider redirects
seonghobae Aug 18, 2026
9637205
test(oidc): preserve callback cancellation
seonghobae Aug 18, 2026
78c3924
fix(oidc): preserve callback cancellation
seonghobae Aug 18, 2026
621eaf6
test(security): model enumerable prototype pollution
seonghobae Aug 18, 2026
f510f33
test(webhook): assert fallback socket pinning
seonghobae Aug 18, 2026
9de1b8a
test(security): isolate static prototype lookup regression
seonghobae Aug 18, 2026
fb6ffe9
test(security): require stable webhook validation code
seonghobae Aug 18, 2026
06d684d
fix(security): expose stable webhook validation code
seonghobae Aug 18, 2026
b3e904c
revert: restore exact webhook core before focused repair
seonghobae Aug 18, 2026
d8841e2
test(security): decouple webhook auth probe from copy
seonghobae Aug 18, 2026
a796553
fix(security): decouple webhook authorization probe from copy
seonghobae Aug 18, 2026
49f68a9
test(security): require stable webhook validation code
seonghobae Aug 18, 2026
fe103bf
test(security): reject arbitrary authorization-probe 400
seonghobae Aug 18, 2026
9601679
fix(security): make webhook auth probe explicit
seonghobae Aug 18, 2026
79c7463
test(security): authenticate before webhook body parsing
seonghobae Aug 18, 2026
41d979b
test(security): bound invalid-token webhook buffering
seonghobae Aug 18, 2026
ebcd3f6
fix(security): bound pre-auth webhook body reads
seonghobae Aug 18, 2026
612c0b9
fix(security): preserve unauthenticated request streams
seonghobae Aug 18, 2026
88891ed
test(oidc): require discovered provider endpoints
seonghobae Aug 18, 2026
366d732
fix(oidc): honor discovered provider endpoints
seonghobae Aug 18, 2026
bd97e40
test(oidc): reproduce metadata-driven private endpoint SSRF
seonghobae Aug 18, 2026
451267a
feat(security): expose DNS-pinned public HTTPS transport
seonghobae Aug 18, 2026
17eb13e
fix(oidc): pin metadata-driven HTTPS requests to public DNS
seonghobae Aug 18, 2026
b687055
test(security): cover reusable public HTTPS transport
seonghobae Aug 18, 2026
09799da
test(auth): reproduce production mock OIDC exposure
seonghobae Aug 18, 2026
17419b2
test(security): bound OIDC state capacity
seonghobae Aug 18, 2026
3b7fc68
fix(security): cap pending OIDC state entries
seonghobae Aug 18, 2026
9ccfb75
test(security): expose core OIDC state growth
seonghobae Aug 18, 2026
5281e04
test(security): execute core OIDC capacity regression
seonghobae Aug 18, 2026
1f4ee85
fix(auth): bound core OIDC state capacity
seonghobae Aug 18, 2026
afad9f2
test(auth): reject production OIDC mock fallback
seonghobae Aug 18, 2026
4ef7d2e
test(auth): execute OIDC mock production regression
seonghobae Aug 18, 2026
b123497
test(auth): exercise public OIDC facade
seonghobae Aug 18, 2026
a61e107
fix(auth): disable mock OIDC outside development
seonghobae Aug 18, 2026
f637f5e
test(oidc): require bounded JWKS cache reuse
seonghobae Aug 18, 2026
5bfb2be
fix(oidc): cache validated signing key evidence
seonghobae Aug 18, 2026
9fbdbae
test(oidc): reject state at exact expiry boundary
seonghobae Aug 18, 2026
15cca54
test(oidc): prevent metadata replay after response
seonghobae Aug 18, 2026
2ffc599
test(ci): register OIDC production and transport regressions
seonghobae Aug 18, 2026
21295f5
test(oidc): preserve fail-closed 404 contract
seonghobae Aug 18, 2026
8e1221a
fix(oidc): prevent response replay and compression
seonghobae Aug 18, 2026
4d71b5c
test(oidc): cover inclusive state expiry boundary
seonghobae Aug 18, 2026
5b0c3af
fix(oidc): expire state inclusively at TTL boundary
seonghobae Aug 18, 2026
2384a78
test(stack): preserve tenant-bound orchestrator attribution
seonghobae Aug 19, 2026
31eb3fa
test(stack): register tenant attribution regression
seonghobae Aug 19, 2026
079e821
fix(stack): preserve tenant-bound orchestrator attribution
seonghobae Aug 19, 2026
6460845
revert(stack): restore exact pre-repair core before safe reconciliation
seonghobae Aug 19, 2026
b26a22c
fix(stack): reconcile protected orchestrator contract into webhook se…
seonghobae Aug 19, 2026
fba15e2
test(api): preserve Request semantics in orchestrator attribution stub
seonghobae Aug 19, 2026
74ad076
test(transport): reject POST replay after TLS connect
seonghobae Aug 19, 2026
2be68fd
fix(transport): prevent ambiguous POST replay
seonghobae Aug 19, 2026
c356313
test(webhook): cover post-handshake no-replay
seonghobae Aug 19, 2026
96ffbac
fix(stack): reconcile SSRF boundary with protected dependency bump
seonghobae Aug 20, 2026
795b516
fix(stack): reconcile SSRF boundary with current develop
seonghobae Aug 20, 2026
e8531e1
fix(stack): preserve protected Playwright version
seonghobae Aug 20, 2026
7fb7d7a
fix(stack): preserve protected Playwright lockfile
seonghobae Aug 20, 2026
6437202
test(transport): reproduce null-body response deadlock
seonghobae Aug 20, 2026
60bed47
fix(transport): settle null-body responses safely
seonghobae Aug 20, 2026
adf83bd
test(auth): preserve legacy email identity compatibility
seonghobae Aug 20, 2026
71ab96b
fix(auth): retain legacy email account reachability
seonghobae Aug 20, 2026
ccae5fe
test(security): reject OIDC flow at exact expiry
seonghobae Aug 20, 2026
d6c61b8
fix(security): expire OIDC bindings inclusively
seonghobae Aug 20, 2026
a0a3220
test(security): isolate facade OIDC expiry boundary
seonghobae Aug 20, 2026
ba61032
test(oidc): reject future not-before ID tokens
seonghobae Aug 22, 2026
b726df8
fix(oidc): enforce ID token not-before
seonghobae Aug 22, 2026
01833c5
test(observability): cover facade rejection accounting
seonghobae Aug 22, 2026
760ba5c
fix(observability): run webhook policy guard on real POST
seonghobae Aug 22, 2026
c668ada
test(observability): cover facade OIDC rejection
seonghobae Aug 22, 2026
83b7d69
fix: preserve probe errors and facade observability
seonghobae Aug 22, 2026
d9da6f3
test(metrics): reproduce missing facade Prometheus counts
seonghobae Aug 22, 2026
5d561ef
fix(metrics): merge facade counts into Prometheus
seonghobae Aug 22, 2026
ac6af17
test(oidc): reproduce multi-key JWKS cache thrash
seonghobae Aug 22, 2026
f91efeb
fix(oidc): bound signing-key cache per kid
seonghobae Aug 22, 2026
30dc201
test(auth): reproduce unbounded public-body buffering
seonghobae Aug 22, 2026
d11340b
fix(auth): bound public authentication bodies
seonghobae Aug 22, 2026
e0de2b4
test(webhooks): reproduce legacy HTTP migration gap
seonghobae Aug 22, 2026
fb99534
test(webhooks): register legacy HTTP migration regression
seonghobae Aug 22, 2026
c1e72a2
fix(webhooks): fail closed legacy HTTP subscriptions
seonghobae Aug 22, 2026
94b51d2
test(webhooks): normalize sqlite migration rows
seonghobae Aug 22, 2026
49ddec8
test(security): reject caller webhook content-length framing
seonghobae Aug 22, 2026
80f6355
fix(security): own webhook content-length framing
seonghobae Aug 22, 2026
3da8155
test(security): reject stale public transport content-length
seonghobae Aug 22, 2026
1dd7815
fix(security): own public transport content length
seonghobae Aug 22, 2026
f92fdc6
test(security): reject unverified OIDC email identities
seonghobae Aug 22, 2026
bc5678a
test(security): run OIDC email verification regression
seonghobae Aug 22, 2026
e62f5c3
fix(security): require verified OIDC email claims
seonghobae Aug 22, 2026
d153ec9
test(oidc): mark valid email fixtures verified
seonghobae Aug 22, 2026
5bde3f3
test(auth): require stable OIDC subject identity
seonghobae Aug 22, 2026
96b7af6
feat(auth): persist stable OIDC identities
seonghobae Aug 22, 2026
de3d041
fix(auth): bind OIDC sessions to issuer subject
seonghobae Aug 22, 2026
37f6625
test(auth): include OIDC identity coverage
seonghobae Aug 22, 2026
6f5beb3
test(auth): order OIDC takeover before email change
seonghobae Aug 22, 2026
f1d2731
test(auth): reject implicit OIDC password-account linking
seonghobae Aug 22, 2026
5dc1561
fix(auth): refuse implicit OIDC account adoption
seonghobae Aug 22, 2026
a5eb7a3
test(oidc): normalize sqlite rows for strict identity assertion
seonghobae Aug 22, 2026
0850c24
test(oidc): require atomic first-login identity binding
seonghobae Aug 22, 2026
8c6c12b
fix(oidc): bind first login atomically
seonghobae Aug 22, 2026
dc1b61d
test(oidc): preserve signup accounting across atomic bind
seonghobae Aug 22, 2026
9a10ba3
test(oidc): assert fail-closed atomic bind response
seonghobae Aug 22, 2026
2b1ec44
fix(metrics): count first-time OIDC signups
seonghobae Aug 22, 2026
2b80d83
test(fuzz): reproduce truncated iteration budgets
seonghobae Aug 22, 2026
2ad7da7
fix(fuzz): honor bounded iteration budgets
seonghobae Aug 22, 2026
bf9e2f0
test(oidc): reproduce whitespace shadow-account split
seonghobae Aug 22, 2026
1b76803
test(oidc): require canonical verified email claims
seonghobae Aug 22, 2026
ba2aab4
fix(oidc): reject non-canonical verified email claims
seonghobae Aug 22, 2026
11eea72
test(observability): isolate webhook authorization probes
seonghobae Aug 23, 2026
dde219f
fix(observability): isolate webhook authorization probes
seonghobae Aug 23, 2026
c26f197
test: reproduce transient sqlite startup lock
seonghobae Aug 23, 2026
3fc64ee
test: correct sqlite lock readiness marker
seonghobae Aug 23, 2026
df1b14b
fix: tolerate transient sqlite writer locks
seonghobae Aug 23, 2026
5aa0cb2
test: reject process-wide console observability mutation
seonghobae Aug 25, 2026
3b8b9c0
test: register console observability regression
seonghobae Aug 25, 2026
fdb2ab5
fix: stop mutating process-wide console logging
seonghobae Aug 25, 2026
0353796
test: reject process-wide fetch interposition
seonghobae Aug 26, 2026
a8fea65
test: register fetch ownership regression
seonghobae Aug 26, 2026
b166825
fix(security): make outbound fetch boundary module-local
seonghobae Aug 26, 2026
03a0f68
test(accessibility): allow facade transport imports
seonghobae Aug 26, 2026
ea40604
test(server): normalize sqlite delivery rows
seonghobae Aug 26, 2026
f53a979
test: cover legacy private HTTPS webhook migration
seonghobae Aug 27, 2026
9ac687d
test: run legacy private webhook migration regression
seonghobae Aug 27, 2026
3af41ed
fix: quarantine legacy webhook destinations by policy
seonghobae Aug 27, 2026
ef8af83
fix(security): quarantine legacy private webhooks
seonghobae Aug 27, 2026
6228733
test(webhooks): reproduce no-op private migration lock
seonghobae Aug 27, 2026
9659689
fix(webhooks): avoid no-op migration writer lock
seonghobae Aug 27, 2026
92487d1
fix(db): consolidate webhook startup reconciliation
seonghobae Aug 27, 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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Security

- Hardened tenant-configured outbound webhooks against SSRF and DNS rebinding by
requiring production HTTPS destinations, rejecting credentials, fragments,
local/private/link-local/special-use address space, revalidating every A/AAAA
answer before each delivery attempt, pinning the actual HTTPS socket to the
validated address while preserving hostname/TLS authority, refusing redirects,
and exposing stable non-secret resolver and transport failures.
- Made contextual-orchestrator briefing requests fail closed unless an authenticated endpoint is configured. Deterministic generated text is restricted to explicit `SCOPEWEAVE_DEV=1`, message/provider responses are bounded and validated, and non-loopback HTTP transport is rejected.
- Made `SCOPEWEAVE_JWT_SECRET` mandatory at startup and rejected weak or
unexpanded placeholder values so production deployments fail closed.
Expand Down
153 changes: 153 additions & 0 deletions docs/doctoring/outbound-webhook-ssrf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Outbound webhook SSRF boundary: evidence and design record

## Status boundary

As of this active repair, protected `develop@1fadec04195805722829b386475a09a15f8cd926`
still contains the historical webhook implementation that accepts arbitrary
`http(s)` destinations and later supplies the persisted URL to server-side
`fetch()`. That is protected-shipped truth until this pull request is integrated.

The active `fix/webhook-ssrf-551` pull request introduces the candidate repair
described below. Nothing in this record represents a certification claim or a
statement that protected `develop` already contains the repair.

## Buyer-visible risk

A custom webhook is intentionally an outbound server-side request whose target
is supplied by a tenant administrator. Without a destination authority boundary,
that feature can become an SSRF primitive against the ScopeWeave runtime,
neighboring private services, link-local cloud metadata endpoints, or other
addresses that are not Internet-facing webhook authorities. Redirect following
and DNS rebinding can invalidate an otherwise correct one-time URL or DNS check.

The commercial requirement is therefore stronger than syntactic URL validation:
ScopeWeave must prove that the address used by the actual socket is an allowed
public destination immediately before every attempt.

## Candidate design

The active repair applies the following fail-closed contract:

1. webhook registration is parsed with the WHATWG `URL` implementation and
production accepts HTTPS only; URL credentials, fragments, localhost-like
names, and literal denied addresses are rejected;
2. every delivery attempt performs a fresh A/AAAA lookup and accepts the attempt
only when **every** returned address passes the same public-address policy;
3. private, loopback, link-local, shared, unspecified, mapped, multicast,
documentation, benchmark, reserved, ULA, and other non-public/special-use
ranges are rejected conservatively using the current IANA special-purpose
registries plus multicast boundaries;
4. the HTTPS socket receives a custom `lookup` callback containing the
just-validated address, while the original DNS hostname remains the HTTP/TLS
authority and SNI name. `agent: false` prevents an older pooled connection
from bypassing the fresh per-attempt authorization;
5. Node's native `https.request()` is used directly; redirects are never
followed, so a 3xx response is a failed delivery rather than authority
transplantation of the signed body and HMAC header;
6. the existing three-second abort budget and bounded retry behavior remain in
the application core. Each retry re-enters the transport and therefore
resolves, validates, and pins again;
7. policy, resolver, TLS, and transport failures expose stable non-secret error
classes rather than internal addresses or resolver/socket details; and
8. the legacy development-only loopback HTTP registration fixture remains
isolated behind `SCOPEWEAVE_DEV=1` solely so the existing failure/retry smoke
path remains deterministic. The outbound transport itself still rejects HTTP,
so that fixture cannot make a loopback connection and production never
inherits it.

The existing Hono route graph is temporarily retained in `server/app_core.mjs`.
`server/app.mjs` is the sole exported server facade and interposes the registration
policy plus the signed-webhook egress transport while delegating unrelated OIDC,
Clearfolio, billing, tenant, and authentication fetches to native `fetch`. This
keeps the security slice bounded and reviewable rather than rewriting unrelated
application behavior inside the same repair.

## Verification contract

Deterministic regression evidence must cover at least:

- production registration rejection for plaintext HTTP, localhost and
`.localhost`, IPv4 loopback in dotted/decimal/hex forms, RFC 1918, IPv4
link-local/metadata-style destinations, IPv6 loopback, ULA, IPv4-mapped IPv6,
URL credentials, and fragments;
- direct public IPv4 and IPv6 literals plus a public-hostname-shaped success seam
without depending on the Internet;
- empty/malformed/private DNS responses and mixed public+private answer sets,
with zero connector calls after a denied resolution;
- a DNS-rebinding sequence where a first public answer can be used but a later
private answer is rejected before the retry socket is created;
- the custom connector lookup returning only the address validated for that
attempt while preserving the original hostname as TLS `servername`;
- redirects treated as failures without a second request;
- stable error text for resolver, synchronous request, asynchronous socket, and
pre-aborted-signal failures; and
- canonical `test:unit`, `test:api`, and c8 registration for every new production
module, while continuing to measure the moved application core rather than
creating a false coverage improvement through a filename split.

A green successor head does not erase the deliberately preserved RED predecessor:
`006cfabda2f9e1b36221215a481b9475a07164c4` registered the real production API
regression and the hosted `unit-and-api` lane failed because protected behavior
returned HTTP 200 for the first denied plaintext-HTTP destination. Exact-current-
head gates must be regenerated after every production or evidence change.

## Standards and primary-source rationale

OWASP identifies custom webhooks as an SSRF use case, recommends validating both
A and AAAA answers when arbitrary external targets are allowed, calls out DNS
pinning/rebinding, and recommends disabling redirects. ScopeWeave therefore does
not rely on registration-time DNS or a second independent resolver decision at
connection time.

IANA's IPv4 and IPv6 Special-Purpose Address Registries are the authoritative
machine-readable inventory for address blocks whose routing or protocol semantics
are exceptional. The registries were last updated October 9, 2025 when this
record was prepared. ScopeWeave uses a conservative deny policy for ranges not
suitable as ordinary public webhook authorities; this includes the IPv6 dummy
prefix `100:0:0:1::/64` added to the registry in 2025.

RFC 6890 establishes the special-purpose address registries and their
`Globally Reachable` semantics. RFC 4291 defines IPv6 unspecified, loopback,
IPv4-mapped, link-local, and multicast semantics. RFC 1918, RFC 3927, and RFC
4193 define private IPv4, IPv4 link-local, and IPv6 unique-local space,
respectively.

Node.js 22 documents that `https.request()` accepts HTTP request options plus TLS
options such as `servername`; its underlying connection options support a custom
DNS `lookup` function. The active repair uses that supported seam so address
validation and socket selection are one authorization decision while TLS still
authenticates the original webhook hostname.

## References

Cotton, M., Vegoda, L., Bonica, R., & Haberman, B. (2013). *Special-purpose IP
address registries* (BCP 153; RFC 6890). Internet Engineering Task Force.
https://doi.org/10.17487/RFC6890

Cheshire, S., Aboba, B., & Guttman, E. (2005). *Dynamic configuration of IPv4
link-local addresses* (RFC 3927). Internet Engineering Task Force.
https://doi.org/10.17487/RFC3927

Hinden, R., & Deering, S. (2006). *IP version 6 addressing architecture* (RFC
4291). Internet Engineering Task Force. https://doi.org/10.17487/RFC4291

Hinden, R., & Haberman, B. (2005). *Unique local IPv6 unicast addresses* (RFC
4193). Internet Engineering Task Force. https://doi.org/10.17487/RFC4193

Internet Assigned Numbers Authority. (2025, October 9). *IPv4 special-purpose
address space*. https://www.iana.org/assignments/iana-ipv4-special-registry/

Internet Assigned Numbers Authority. (2025, October 9). *IPv6 special-purpose
address space*. https://www.iana.org/assignments/iana-ipv6-special-registry/

Open Worldwide Application Security Project Foundation. (n.d.). *Server side
request forgery prevention cheat sheet*. OWASP Cheat Sheet Series. Retrieved
August 18, 2026, from
https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html

OpenJS Foundation. (2026). *HTTPS*. Node.js v22 documentation.
https://nodejs.org/docs/latest-v22.x/api/https.html

Rekhter, Y., Moskowitz, B., Karrenberg, D., de Groot, G. J., & Lear, E. (1996).
*Address allocation for private Internets* (BCP 5; RFC 1918). Internet
Engineering Task Force. https://doi.org/10.17487/RFC1918
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
"check:python-docstrings": "node scripts/ci/static_coverage_evidence.mjs docstrings",
"coverage": "npm run test:coverage",
"server": "node server/server.mjs",
"test:api": "node tests/api/auth-secret.test.mjs && node tests/api/smoke.mjs && node tests/api/ratelimit.test.mjs && node tests/api/attachment-status.test.mjs && node tests/api/session-revocation.test.mjs && node tests/api/orchestrator-attribution.test.mjs",
"test:unit": "node tests/unit/opencode-config.test.mjs && node tests/unit/changelog-release-notes.test.mjs && node tests/unit/analytics.test.mjs && node tests/unit/cpm.test.mjs && node tests/unit/baseline-compare.test.mjs && node tests/unit/workload.test.mjs && node tests/unit/cost-evm.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && node tests/unit/dep-types.test.mjs && node tests/unit/weekly-report.test.mjs && node tests/unit/clearfolio.test.mjs && node tests/unit/clearfolio-adapter-mock-hmac.test.mjs && node tests/unit/orchestrator.test.mjs && node tests/unit/orchestrator-coverage.test.mjs && node tests/unit/orchestrator-attribution.test.mjs && node tests/unit/sprint-stats.test.mjs && node tests/unit/burndown.test.mjs && node tests/unit/pm-analysis.test.mjs && node tests/unit/cloud-sync-security.test.mjs && node tests/unit/attachment-status.test.mjs && node tests/unit/clearfolio-status-signal.test.mjs && node tests/unit/coverage-script-contract.test.mjs && node tests/unit/toast-accessibility.test.mjs",
"test:coverage": "c8 --all --include=app.js --include=cloud-sync.js --include=scripts/ci/static_coverage_evidence.mjs --include=server/attachment_status.mjs --include=server/app.mjs --include=server/auth.mjs --include=server/clearfolio.mjs --include=server/orchestrator.mjs --reporter=json --reporter=json-summary npm run test:coverage:cases",
"test:coverage:cases": "node tests/unit/coverage-script-contract.test.mjs && node tests/unit/attachment-status.test.mjs && node tests/unit/clearfolio-status-signal.test.mjs && node tests/unit/clearfolio-adapter-mock-hmac.test.mjs && node tests/unit/orchestrator.test.mjs && node tests/unit/orchestrator-coverage.test.mjs && node tests/unit/orchestrator-attribution.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && npm run test:api",
"test:api": "node tests/api/auth-secret.test.mjs && node tests/api/smoke.mjs && node tests/api/ratelimit.test.mjs && node tests/api/attachment-status.test.mjs && node tests/api/session-revocation.test.mjs && node tests/api/orchestrator-attribution.test.mjs && node tests/api/static-prototype-pollution.test.mjs && node tests/api/webhook-destination-policy.test.mjs && node tests/api/webhook-legacy-migration.test.mjs && node tests/api/webhook-legacy-private-destination-migration.test.mjs && node tests/api/fetch-boundary-ownership.test.mjs && node tests/api/review-regressions.test.mjs && node tests/api/console-observability-boundary.test.mjs && node tests/api/email-identity-compat.test.mjs && node tests/api/oidc-production-boundary.test.mjs && node tests/api/oidc-state-capacity.test.mjs && node tests/api/oidc-mock-dev-only.test.mjs && node tests/api/oidc-email-verification.test.mjs && node tests/api/oidc-timeout.test.mjs",
"test:unit": "node tests/unit/opencode-config.test.mjs && node tests/unit/changelog-release-notes.test.mjs && node tests/unit/analytics.test.mjs && node tests/unit/cpm.test.mjs && node tests/unit/baseline-compare.test.mjs && node tests/unit/workload.test.mjs && node tests/unit/cost-evm.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && node tests/unit/dep-types.test.mjs && node tests/unit/weekly-report.test.mjs && node tests/unit/clearfolio.test.mjs && node tests/unit/clearfolio-adapter-mock-hmac.test.mjs && node tests/unit/orchestrator.test.mjs && node tests/unit/orchestrator-coverage.test.mjs && node tests/unit/orchestrator-attribution.test.mjs && node tests/unit/sprint-stats.test.mjs && node tests/unit/burndown.test.mjs && node tests/unit/pm-analysis.test.mjs && node tests/unit/cloud-sync-security.test.mjs && node tests/unit/attachment-status.test.mjs && node tests/unit/clearfolio-status-signal.test.mjs && node tests/unit/coverage-script-contract.test.mjs && node tests/unit/toast-accessibility.test.mjs && node tests/unit/webhook-transport.test.mjs && node tests/unit/public-https-transport.test.mjs",
"test:coverage": "c8 --all --include=app.js --include=cloud-sync.js --include=scripts/ci/static_coverage_evidence.mjs --include=server/attachment_status.mjs --include=server/app.mjs --include=server/app_core.mjs --include=server/auth.mjs --include=server/clearfolio.mjs --include=server/oidc_identity.mjs --include=server/orchestrator.mjs --include=server/webhook_transport.mjs --reporter=json --reporter=json-summary npm run test:coverage:cases",
"test:coverage:cases": "node tests/unit/coverage-script-contract.test.mjs && node tests/unit/attachment-status.test.mjs && node tests/unit/clearfolio-status-signal.test.mjs && node tests/unit/clearfolio-adapter-mock-hmac.test.mjs && node tests/unit/orchestrator.test.mjs && node tests/unit/orchestrator-coverage.test.mjs && node tests/unit/orchestrator-attribution.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && node tests/unit/webhook-transport.test.mjs && node tests/unit/public-https-transport.test.mjs && npm run test:api",
"test:e2e": "playwright test",
"test:e2e:headed": "playwright test --headed",
"test:e2e:cloud": "playwright install chromium && playwright test tests/e2e/cloud.spec.js tests/e2e/toast-accessibility.spec.js",
Expand Down
Loading
Loading