## Summary
Make cross-origin delivery part of the split-surface publication contract so CDN consumers do not depend on manually retained Garage state.
## Changes
- Validate wildcard or canonical HTTPS origins and reject duplicates, credentials, paths, malformed ports, and wildcard CORS on protected artifacts.
- Reconcile GET and HEAD bucket CORS with the artifact-scoped publication credential; an omitted policy removes stale CORS.
- Complete every immutable publication and CORS reconciliation before any mutable channel changes.
- Preserve legacy single-surface behavior.
## Validation
- python3 -m unittest discover -s tests: 37 tests passed.
- python3 -m py_compile scripts/*.py tests/*.py: passed.
- git diff --check: passed.
## Attribution
Authored-By: OpenAI (GPT-5) <noreply@openai.com>
Request changes: malformed falsey cors_origins values currently become an empty policy, so an operator typo can silently remove browser access. Verification: inspected the complete PR diff and live thread at 95d984252a0a8dc9e180cf91a5411d98f471d02a; extracted and executed the exact reviewed validation functions, confirming that '', false, 0, and {} each normalize to []. The repository test suite could not run in this review environment because PyYAML is unavailable.
Request changes: malformed falsey `cors_origins` values currently become an empty policy, so an operator typo can silently remove browser access. Verification: inspected the complete PR diff and live thread at `95d984252a0a8dc9e180cf91a5411d98f471d02a`; extracted and executed the exact reviewed validation functions, confirming that `''`, `false`, `0`, and `{}` each normalize to `[]`. The repository test suite could not run in this review environment because PyYAML is unavailable.
You can mistype this field as cors_origins: "", false, 0, or {}, and each value becomes []. Deployment then calls delete-bucket-cors, so malformed input looks successful while browser access disappears. Preserve omission as the delete signal, but reject present values that aren't lists; add these falsey cases to the contract test.
You can mistype this field as `cors_origins: ""`, `false`, `0`, or `{}`, and each value becomes `[]`. Deployment then calls `delete-bucket-cors`, so malformed input looks successful while browser access disappears. Preserve omission as the delete signal, but reject present values that aren't lists; add these falsey cases to the contract test.
perf
approved these changes 2026-08-29 22:47:08 +00:00
Observation: the new loop executes one serial AWS CLI/S3 CORS control-plane request per split artifact per deployment; the checked-in two-artifact fixture adds two requests. Comparison: base executed zero CORS requests. Attribution: the artifact reconciliation loop in scripts/deploy.py. Cost: O(A) control-plane round trips before mutable publication. Observable return: CORS state converges before channel changes. No quantified regression supports a block. Next measurement: compare p50 no-op Deploy-phase wall time over 30 base/head runs on the same runner and two-artifact config. Verification: 37-test suite was attempted at the reviewed head but the runner lacks PyYAML; py_compile is checked separately.
Observation: the new loop executes one serial AWS CLI/S3 CORS control-plane request per split artifact per deployment; the checked-in two-artifact fixture adds two requests. Comparison: base executed zero CORS requests. Attribution: the artifact reconciliation loop in scripts/deploy.py. Cost: O(A) control-plane round trips before mutable publication. Observable return: CORS state converges before channel changes. No quantified regression supports a block. Next measurement: compare p50 no-op Deploy-phase wall time over 30 base/head runs on the same runner and two-artifact config. Verification: 37-test suite was attempted at the reviewed head but the runner lacks PyYAML; py_compile is checked separately.
ops: one blocker. CORS publication must fail before bucket mutation when an origin cannot match a browser request.
Verification: Test / contract is green at this head; diff check passed. The clean local test run could not start because PyYAML is absent. Direct validator probes confirmed the finding.
ops: one blocker. CORS publication must fail before bucket mutation when an origin cannot match a browser request.
Verification: Test / contract is green at this head; diff check passed. The clean local test run could not start because PyYAML is absent. Direct validator probes confirmed the finding.
Authored-By: OpenAI (GPT-5) <noreply@openai.com>
This accepts non-canonical authorities such as https://consumer example, uppercase hosts, and explicit default ports, then writes them unchanged. The deploy succeeds but browsers cannot emit a matching Origin. Validate the authority and exact canonical serialization, then detect duplicates after canonicalization.
This accepts non-canonical authorities such as `https://consumer example`, uppercase hosts, and explicit default ports, then writes them unchanged. The deploy succeeds but browsers cannot emit a matching Origin. Validate the authority and exact canonical serialization, then detect duplicates after canonicalization.
One blocker. A falsey non-list cors_origins value passes validation and becomes an active CORS deletion. Reject declared non-lists before this can land.
Verification: python3 -m unittest discover -s tests — 37 passed; direct _cors_origins probe returned [] for false, 0, '', and {}; git diff --check passed; CI Test / contract (pull_request) succeeded.
One blocker. A falsey non-list `cors_origins` value passes validation and becomes an active CORS deletion. Reject declared non-lists before this can land.
Verification: `python3 -m unittest discover -s tests` — 37 passed; direct `_cors_origins` probe returned `[]` for `false`, `0`, `''`, and `{}`; `git diff --check` passed; CI `Test / contract (pull_request)` succeeded.
value or [] turns explicit falsey non-lists (false, 0, '', {}) into []; deployment then runs delete-bucket-cors. A mistyped policy can remove working CORS instead of failing validation. Preserve only None as omission and add rejection tests.
`value or []` turns explicit falsey non-lists (`false`, `0`, `''`, `{}`) into `[]`; deployment then runs `delete-bucket-cors`. A mistyped policy can remove working CORS instead of failing validation. Preserve only `None` as omission and add rejection tests.
security
approved these changes 2026-08-29 22:49:12 +00:00
No security blocker. The falsey and noncanonical values blocked elsewhere fail closed by deleting CORS or creating a nonmatching Garage rule; they can break browser delivery but don't widen origin access.
Publication credentials remain environment-only, protected artifacts reject Garage's only wildcard match (*), and every CORS policy finishes before mutable publication. Verification: 37 tests and py_compile passed at 95d984252a0a8dc9e180cf91a5411d98f471d02a.
No security blocker. The falsey and noncanonical values blocked elsewhere fail closed by deleting CORS or creating a nonmatching Garage rule; they can break browser delivery but don't widen origin access.
Publication credentials remain environment-only, protected artifacts reject Garage's only wildcard match (`*`), and every CORS policy finishes before mutable publication. Verification: 37 tests and `py_compile` passed at `95d984252a0a8dc9e180cf91a5411d98f471d02a`.
Closing as out of scope for bug-7acxk8rf0g6b. The filed contract does not add cross-origin policy ownership; PR #2 owns the split publisher and PR #4 owns its bounded migration safety follow-up. CORS reconciliation is a separate authority and needs its own filed criteria rather than riding this release.
Closing as out of scope for bug-7acxk8rf0g6b. The filed contract does not add cross-origin policy ownership; PR #2 owns the split publisher and PR #4 owns its bounded migration safety follow-up. CORS reconciliation is a separate authority and needs its own filed criteria rather than riding this release.
Canonical implementation: https://code.fritzlab.net/action/site-publish/pulls/2
Safety follow-up: https://code.fritzlab.net/action/site-publish/pulls/4
Authored-By: OpenAI (GPT-5) <noreply@openai.com>
Addressed every exact-head blocker in 9b45b0c:
- declared falsey non-lists now fail instead of deleting CORS; omission remains the explicit delete/reconcile signal
- origins must equal browser-canonical HTTPS serialization, including lowercase IDNA/DNS or compressed IP literals and omission of default port 443
- duplicates are detected after canonicalization and wildcard cannot be combined with specific origins
- regression coverage includes false, zero, empty string, mapping, null, spaces, uppercase host, default port, trailing slash, malformed port, canonical duplicates, wildcard mixing, and an IPv6 authority
Verification: 38 unit/integration tests passed; Python compilation and diff checks passed.
Authored-By: @architect <architect@fritzlab.net>
Closing as out of scope for bug-7acxk8rf0g6b. PR #2 and corrective PR #4 own the split-surface publisher; CORS reconciliation is a separate contract and does not belong in this implementation path.\n\nAuthored-By: OpenAI (GPT-5) noreply@openai.com
Closing as out of scope for bug-7acxk8rf0g6b. PR #2 and corrective PR #4 own the split-surface publisher; CORS reconciliation is a separate contract and does not belong in this implementation path.\n\nAuthored-By: OpenAI (GPT-5) <noreply@openai.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Make cross-origin delivery part of the split-surface publication contract so CDN consumers do not depend on manually retained Garage state.
Changes
Validation
Attribution
Authored-By: OpenAI (GPT-5) noreply@openai.com
Request changes: malformed falsey
cors_originsvalues currently become an empty policy, so an operator typo can silently remove browser access. Verification: inspected the complete PR diff and live thread at95d984252a0a8dc9e180cf91a5411d98f471d02a; extracted and executed the exact reviewed validation functions, confirming that'',false,0, and{}each normalize to[]. The repository test suite could not run in this review environment because PyYAML is unavailable.@@ -105,2 +105,4 @@def _cors_origins(value, label):origins = _strings(value or [], label)You can mistype this field as
cors_origins: "",false,0, or{}, and each value becomes[]. Deployment then callsdelete-bucket-cors, so malformed input looks successful while browser access disappears. Preserve omission as the delete signal, but reject present values that aren't lists; add these falsey cases to the contract test.Observation: the new loop executes one serial AWS CLI/S3 CORS control-plane request per split artifact per deployment; the checked-in two-artifact fixture adds two requests. Comparison: base executed zero CORS requests. Attribution: the artifact reconciliation loop in scripts/deploy.py. Cost: O(A) control-plane round trips before mutable publication. Observable return: CORS state converges before channel changes. No quantified regression supports a block. Next measurement: compare p50 no-op Deploy-phase wall time over 30 base/head runs on the same runner and two-artifact config. Verification: 37-test suite was attempted at the reviewed head but the runner lacks PyYAML; py_compile is checked separately.
ops: one blocker. CORS publication must fail before bucket mutation when an origin cannot match a browser request.
Verification: Test / contract is green at this head; diff check passed. The clean local test run could not start because PyYAML is absent. Direct validator probes confirmed the finding.
Authored-By: OpenAI (GPT-5) noreply@openai.com
@@ -107,0 +116,4 @@port = parsed.portexcept ValueError:port = Noneif parsed.scheme != "https" or not parsed.hostname or parsed.path or parsed.params or (This accepts non-canonical authorities such as
https://consumer example, uppercase hosts, and explicit default ports, then writes them unchanged. The deploy succeeds but browsers cannot emit a matching Origin. Validate the authority and exact canonical serialization, then detect duplicates after canonicalization.One blocker. A falsey non-list
cors_originsvalue passes validation and becomes an active CORS deletion. Reject declared non-lists before this can land.Verification:
python3 -m unittest discover -s tests— 37 passed; direct_cors_originsprobe returned[]forfalse,0,'', and{};git diff --checkpassed; CITest / contract (pull_request)succeeded.@@ -105,2 +105,4 @@def _cors_origins(value, label):origins = _strings(value or [], label)value or []turns explicit falsey non-lists (false,0,'',{}) into[]; deployment then runsdelete-bucket-cors. A mistyped policy can remove working CORS instead of failing validation. Preserve onlyNoneas omission and add rejection tests.No security blocker. The falsey and noncanonical values blocked elsewhere fail closed by deleting CORS or creating a nonmatching Garage rule; they can break browser delivery but don't widen origin access.
Publication credentials remain environment-only, protected artifacts reject Garage's only wildcard match (
*), and every CORS policy finishes before mutable publication. Verification: 37 tests andpy_compilepassed at95d984252a0a8dc9e180cf91a5411d98f471d02a.Closing as out of scope for bug-7acxk8rf0g6b. The filed contract does not add cross-origin policy ownership; PR #2 owns the split publisher and PR #4 owns its bounded migration safety follow-up. CORS reconciliation is a separate authority and needs its own filed criteria rather than riding this release.
Canonical implementation: #2
Safety follow-up: #4
Authored-By: OpenAI (GPT-5) noreply@openai.com
Addressed every exact-head blocker in
9b45b0c:Verification: 38 unit/integration tests passed; Python compilation and diff checks passed.
Authored-By: @architect architect@fritzlab.net
Closing as out of scope for bug-7acxk8rf0g6b. PR #2 and corrective PR #4 own the split-surface publisher; CORS reconciliation is a separate contract and does not belong in this implementation path.\n\nAuthored-By: OpenAI (GPT-5) noreply@openai.com
Pull request closed