## The Problem werkcrappit's code city page links a CodeCharta viewer with the URL of the `cc.json` beside it. Served by Werkator, the viewer at javagil.de could not load it: the browser blocks a script from reading another site's response unless that site allows it with `Access-Control-Allow-Origin`. CodeCharta reported only `Http failure response … 0 Unknown Error` and loaded its sample files instead. ## Non-Goals - Credentials: no `Access-Control-Allow-Credentials`, so a private repository's artifacts stay unreadable from other sites. - Preflight handling: a plain `GET` without own headers needs none. - Other routes than artifact files: pages and the API keep answering without CORS. ## The Scenarios #### Scenario#96.01: an artifact file answers an allowed origin … so that a viewer on another host, such as CodeCharta at javagil.de, loads a code city straight from Werkator. - **Given** `server.corsAllowedOrigins` in the instance file lists `https://javagil.de` - **When** the browser fetches an artifact file of a public repository with `Origin: https://javagil.de` - **Then** the response carries `Access-Control-Allow-Origin: https://javagil.de` and `Vary: Origin` - **And** no `Access-Control-Allow-Credentials`, so a token or cookie never reaches the other origin ##### Verified by - [ArtifactFileControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/ArtifactFileControllerRestTest.kt) `an artifact file answers an allowed origin, without credentials` #### Scenario#96.02: no header for other origins or without the setting … so that Werkator shares nothing with a website the instance did not name. - **Given** `server.corsAllowedOrigins` lists `https://javagil.de` - **When** the browser fetches an artifact file with `Origin: https://evil.example`, or without `Origin` - **Then** the response carries no `Access-Control-Allow-Origin` - **And** without the setting, the default, no response carries one, as before ##### Verified by - [ArtifactFileControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/ArtifactFileControllerRestTest.kt) `another origin, or none, gets no CORS header` - [ConfigLoaderUnitTest](../../src/test/kotlin/de/hoennig/werkator/config/ConfigLoaderUnitTest.kt) `without a setting no origin may read artifacts` #### Scenario#96.03: a repository cannot widen the setting … so that only the operator of an instance decides which websites may read its artifacts. - **Given** a repository's `.werkator.yml`, committed or on a branch, sets `server.corsAllowedOrigins` - **Then** that value is ignored, with the same warning as other `server` keys outside the instance file - **And** a private repository still answers 404 without the control token, `Origin` or not ##### Verified by - [ConfigLoaderUnitTest](../../src/test/kotlin/de/hoennig/werkator/config/ConfigLoaderUnitTest.kt) `the origins allowed to read artifacts are the instance's` - [ArtifactFileControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/ArtifactFileControllerRestTest.kt) `a private repository stays hidden to an allowed origin` ## The Solution - `ServerConfig.corsAllowedOrigins`, a list of exact origins, empty by default. The `server` section is instance-level: with an instance file, a repository's copy is ignored with a warning, and a branch layer never carries it. Without an instance file, the repository's committed file is the instance's, as for every `server` key. - `ArtifactFileController` answers both artifact routes, by key and the permanent branch URL, with the request's `Origin` when it is listed, and always with `Vary: Origin`, so a cache never hands one site's answer to another. - The commented `init` template and `docs/configuration.md` name the key. ## Open Questions - mih09 needs `server.corsAllowedOrigins: ["https://javagil.de", "https://codecharta.com"]` in `~/.werkator.yml`; read per request, no restart needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #96
4.0 KiB
PR#96: Artifacts Readable From Allowed Websites
This document describes the change of this pull request at the time it was made. Later pull requests may change the behaviour without updating it.
The Problem
werkcrappit's code city page links a CodeCharta viewer with the URL of the cc.json beside it.
Served by Werkator, the viewer at javagil.de could not load it: the browser blocks a script from reading another site's response unless that site allows it with Access-Control-Allow-Origin.
CodeCharta reported only Http failure response … 0 Unknown Error and loaded its sample files instead.
Non-Goals
- Credentials: no
Access-Control-Allow-Credentials, so a private repository's artifacts stay unreadable from other sites. - Preflight handling: a plain
GETwithout own headers needs none. - Other routes than artifact files: pages and the API keep answering without CORS.
The Scenarios
Scenario#96.01: an artifact file answers an allowed origin
… so that a viewer on another host, such as CodeCharta at javagil.de, loads a code city straight from Werkator.
- Given
server.corsAllowedOriginsin the instance file listshttps://javagil.de - When the browser fetches an artifact file of a public repository with
Origin: https://javagil.de - Then the response carries
Access-Control-Allow-Origin: https://javagil.deandVary: Origin - And no
Access-Control-Allow-Credentials, so a token or cookie never reaches the other origin
Verified by
- ArtifactFileControllerRestTest
an artifact file answers an allowed origin, without credentials
Scenario#96.02: no header for other origins or without the setting
… so that Werkator shares nothing with a website the instance did not name.
- Given
server.corsAllowedOriginslistshttps://javagil.de - When the browser fetches an artifact file with
Origin: https://evil.example, or withoutOrigin - Then the response carries no
Access-Control-Allow-Origin - And without the setting, the default, no response carries one, as before
Verified by
- ArtifactFileControllerRestTest
another origin, or none, gets no CORS header - ConfigLoaderUnitTest
without a setting no origin may read artifacts
Scenario#96.03: a repository cannot widen the setting
… so that only the operator of an instance decides which websites may read its artifacts.
- Given a repository's
.werkator.yml, committed or on a branch, setsserver.corsAllowedOrigins - Then that value is ignored, with the same warning as other
serverkeys outside the instance file - And a private repository still answers 404 without the control token,
Originor not
Verified by
- ConfigLoaderUnitTest
the origins allowed to read artifacts are the instance's - ArtifactFileControllerRestTest
a private repository stays hidden to an allowed origin
The Solution
ServerConfig.corsAllowedOrigins, a list of exact origins, empty by default. Theserversection is instance-level: with an instance file, a repository's copy is ignored with a warning, and a branch layer never carries it. Without an instance file, the repository's committed file is the instance's, as for everyserverkey.ArtifactFileControlleranswers both artifact routes, by key and the permanent branch URL, with the request'sOriginwhen it is listed, and always withVary: Origin, so a cache never hands one site's answer to another.- The commented
inittemplate anddocs/configuration.mdname the key.
Open Questions
- mih09 needs
server.corsAllowedOrigins: ["https://javagil.de", "https://codecharta.com"]in~/.werkator.yml; read per request, no restart needed.