## 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
71 lines
4.0 KiB
Markdown
71 lines
4.0 KiB
Markdown
# 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 `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.
|