artifactDirs is a branch setting, and the server copied each entry on the host,
outside the sandbox: an absolute path or '..' made it copy any host directory,
e.g. the machine config with the Gitea token, into the build's artifacts.
ConfigLoader now refuses such an entry, and FileArtifactStore checks again
before the copy, with the real path, so a symlink in the workspace cannot escape.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ai-review/assessment AI findings; a developer approval after checking them unlocks the merge; review requested from mi; merge scheduled for when the checks succeed
ORANGE 1 finding(s) that Jev rates as probably real and severe: src/main/kotlin/de/javagil/werkator/artifacts/FileArtifactStore.kt:182-189
YELLOW model reports a security concern: Host file disclosure: the new containment check still permits a symlinked artifact root to bypass descendant checks, allowing a branch build to cause service-account files to be copied into its artifacts.
hint 7 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (all full files together would exceed [review] max_full_files_chars = 160000): src/test/kotlin/de/javagil/werkator/artifacts/FileArtifactStoreUnitTest.kt
hint model recommends changes before merge
hint model rates the risk as high (advisory: Jev judged every changed file)
Findings
[high] Symlink Bypass at src/main/kotlin/de/javagil/werkator/artifacts/FileArtifactStore.kt:182-189, ORANGE, as a line comment
Classifier
7 of 7 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 55291, calls 1, tokens in 53058 / out 48700 (reasoning included in out), duration 439.7 s, cost 0.0743 USD (reported), classifier `jev-1.14.0` calls 7, tokens in 5807, cost 0.0002 USD, finding judge `jev-1.14.0` calls 1, tokens in 2462, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 63003 / out 722, cost 0.0082 USD (reported), build file judge `jev-1.14.0` calls 8, tokens in 10963, cost 0.0005 USD, phases: path_judge 0.4 s, classifier 0.3 s, context 12.6 s, model_review 425.6 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 9e6dff6).
Reviewed head `e4cccbd23180207905e85569cc7a0b88d3b9a02a` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
<!-- werkrevisor:e4cccbd23180207905e85569cc7a0b88d3b9a02a -->
<!-- werkrevisor-verdict:ORANGE -->
<!-- werkrevisor-blocks:90fe99b121c86774ab17d8dc30ca8495,86987efbec7a9183d411aff7919fa36c,533ee0386fa955759ec62e933d20001e,b99d6d2400048e361888e4ddf5e87b67,d58a827abd669efc157ba527ff8254e5,7b4e23d19ce1717045c7ee0cd4ab5bff,38d98fcda6af26b3361805efdc4f0c6e,2d368f08a235eeb850c66b29dfa6a8e0,3ec718b5c0c9aacaff755f84e2a791f6,eba40ddddaa33c1e8e5bd74da341e86f,16b1e83b5b4d7481a45a7e34bd9272b1,0c76ac4965fb6bc41dfbf3f18ce0c7d4,c9261ce9e650f6ac894ecff4b6d9274d,7d8c46ea15cd46cdf90f44e1c71f89b8,fb8c6a0d97a0f1b3bade9e3967f25511,6dbc4fd47ad4d90bae85b8db6ec67669,c28c418cbbafbce9e8b8799322906211 -->
<!-- werkrevisor-folded -->
<details><summary>🟠 werkrevisor ORANGE for <code>e4cccbd</code>, superseded by <a href="http://git.javagil.de/mi/werkator/pulls/167#issuecomment-7756">the result for <code>5c6a245</code></a></summary>
## 🟠 werkrevisor: ORANGE / FINDINGS
Change kind: `mixed`.
### Reasons
- **ORANGE** 1 finding(s) that Jev rates as probably real and severe: src/main/kotlin/de/javagil/werkator/artifacts/FileArtifactStore.kt:182-189
- **YELLOW** model reports a security concern: Host file disclosure: the new containment check still permits a symlinked artifact root to bypass descendant checks, allowing a branch build to cause service-account files to be copied into its artifacts.
- **hint** 7 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (all full files together would exceed [review] max_full_files_chars = 160000): src/test/kotlin/de/javagil/werkator/artifacts/FileArtifactStoreUnitTest.kt
- **hint** model recommends changes before merge
- **hint** model rates the risk as high (advisory: Jev judged every changed file)
### Findings
1. **[high] Symlink Bypass** at `src/main/kotlin/de/javagil/werkator/artifacts/FileArtifactStore.kt:182-189`, ORANGE, as a line comment
### Classifier
7 of 7 units passed all questions (1 PR document(s)).
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 55291, calls 1, tokens in 53058 / out 48700 (reasoning included in out), duration 439.7 s, cost 0.0743 USD (reported), classifier `jev-1.14.0` calls 7, tokens in 5807, cost 0.0002 USD, finding judge `jev-1.14.0` calls 1, tokens in 2462, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 63003 / out 722, cost 0.0082 USD (reported), build file judge `jev-1.14.0` calls 8, tokens in 10963, cost 0.0005 USD, phases: path_judge 0.4 s, classifier 0.3 s, context 12.6 s, model_review 425.6 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 9e6dff6).<br>
Reviewed head `e4cccbd23180207905e85569cc7a0b88d3b9a02a` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
</sub>
</details>
werkrevisor: ORANGE / FINDINGS for e4cccbd23180207905e85569cc7a0b88d3b9a02a.
The result comment of werkrevisor holds the details.
werkrevisor: ORANGE / FINDINGS for `e4cccbd23180207905e85569cc7a0b88d3b9a02a`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:e4cccbd23180207905e85569cc7a0b88d3b9a02a -->
containedSource() validates the resolved directory but returns the unresolved source. If artifactDirs names a symlink to another directory inside the workspace, Files.walkFileTree(source) visits only that symlink, while the subsequent Files.copy() follows it and recursively copies the target without checking its descendants. A build can create artifacts -> real-artifacts and real-artifacts/token -> /home/werkator/.config/token; the root check passes, and the host token is copied into publicly served artifacts. Copy the resolved path through the guarded traversal, or reject symlinked artifact roots, and add this nested case to the tests.
**[high] Symlink Bypass** (werkrevisor)
ORANGE; Jev: real 0.81, severity 3.0 of 3
`containedSource()` validates the resolved directory but returns the unresolved `source`. If `artifactDirs` names a symlink to another directory inside the workspace, `Files.walkFileTree(source)` visits only that symlink, while the subsequent `Files.copy()` follows it and recursively copies the target without checking its descendants. A build can create `artifacts -> real-artifacts` and `real-artifacts/token -> /home/werkator/.config/token`; the root check passes, and the host token is copied into publicly served artifacts. Copy the resolved path through the guarded traversal, or reject symlinked artifact roots, and add this nested case to the tests.
Fixed in 5c6a245: containedSource returns the checked real path, and the copy walks it, so the link check of copyRecursively covers everything below an entry that is itself a link.
The described leak did not occur before: walkFileTree does not follow a start that is a link and hands it to visitFile, where Files.copy creates only an empty directory, without its entries.
But the guarantee hung on that detail, and such an entry stored nothing useful either.
Test: FileArtifactStoreUnitTest, "an artifact directory that is a link inside the workspace is collected from its target, links below checked"; it fails without the fix.
Fixed in `5c6a245`: `containedSource` returns the checked real path, and the copy walks it, so the link check of `copyRecursively` covers everything below an entry that is itself a link.
The described leak did not occur before: `walkFileTree` does not follow a start that is a link and hands it to `visitFile`, where `Files.copy` creates only an empty directory, without its entries.
But the guarantee hung on that detail, and such an entry stored nothing useful either.
Test: `FileArtifactStoreUnitTest`, "an artifact directory that is a link inside the workspace is collected from its target, links below checked"; it fails without the fix.
claude-code marked this conversation as resolved
ai-reviewer
scheduled this pull request to auto merge when all checks succeed 2026-10-24 19:53:04 +02:00
ai-reviewer
requested review from mi 2026-10-24 19:53:04 +02:00
[medium] Artifact-path rejection during collection leaves the build successful (Codex review)
If a build command changes its workspace’s .werkator.yml to contain artifactDirs: [/home/werkator] and exits successfully, artifact collection reloads that configuration and throws ConfigFormatException here before reaching containedSource. BuildExecutor.storeArtifacts only changes the status for ArtifactEscapeException; its generic catch merely logs this exception. Consequently, nothing is archived but the build and Gitea status remain SUCCESS, contrary to the promised failure for rejected artifact paths. The new store test mocks ConfigLoader, bypassing this actual exception path.
Fix it, or reply why it does not hold, and resolve this comment.
**[medium] Artifact-path rejection during collection leaves the build successful** (Codex review)
If a build command changes its workspace’s .werkator.yml to contain artifactDirs: [/home/werkator] and exits successfully, artifact collection reloads that configuration and throws ConfigFormatException here before reaching containedSource. BuildExecutor.storeArtifacts only changes the status for ArtifactEscapeException; its generic catch merely logs this exception. Consequently, nothing is archived but the build and Gitea status remain SUCCESS, contrary to the promised failure for rejected artifact paths. The new store test mocks ConfigLoader, bypassing this actual exception path.
Fix it, or reply why it does not hold, and resolve this comment.
<!-- werkrevisor-codex -->
Fixed in 5c6a245: FileArtifactStore.buildSettings turns a ConfigException at collection into an ArtifactEscapeException, so BuildExecutor.storeArtifacts takes the build's success back.
Test with the real ConfigLoader: FileArtifactStoreUnitTest, "a configuration the build rewrote to an artifact directory outside its workspace fails the collection"; it fails without the fix.
Fixed in `5c6a245`: `FileArtifactStore.buildSettings` turns a `ConfigException` at collection into an `ArtifactEscapeException`, so `BuildExecutor.storeArtifacts` takes the build's success back.
Test with the real `ConfigLoader`: `FileArtifactStoreUnitTest`, "a configuration the build rewrote to an artifact directory outside its workspace fails the collection"; it fails without the fix.
This changes the security boundary between branch-controlled artifact paths and host filesystem access. A mistake could expose service credentials through publicly served artifacts, so human review is required.
Findings
[medium] Artifact-path rejection during collection leaves the build successful at src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:220, as a line comment
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 265649 (cached 203392) / out 2005, 74.7 s, 11 commands; reviewed head `e4cccbd23180207905e85569cc7a0b88d3b9a02a`.
<!-- werkrevisor-codex:e4cccbd23180207905e85569cc7a0b88d3b9a02a -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the security boundary between branch-controlled artifact paths and host filesystem access. A mistake could expose service credentials through publicly served artifacts, so human review is required.
### Findings
1. **[medium] Artifact-path rejection during collection leaves the build successful** at `src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:220`, as a line comment
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 265649 (cached 203392) / out 2005, 74.7 s, 11 commands; reviewed head `e4cccbd23180207905e85569cc7a0b88d3b9a02a`.
</sub>
Review of werkrevisor: an artifact directory that is itself a link was walked as the link,
so the check of the links below it depended on how walkFileTree treats its start.
Review of Codex: a configuration the build rewrote to a refused entry left the build green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hint 7 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
hint 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
hint 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
hint 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (all full files together would exceed [review] max_full_files_chars = 160000): src/test/kotlin/de/javagil/werkator/artifacts/FileArtifactStoreUnitTest.kt
Classifier
7 of 7 units passed all questions (1 PR document(s)).
2 answers ignored because of a confidence below [classifier] min_confidence.
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 57749, calls 1, tokens in 55407 / out 49842 (reasoning included in out), duration 430.0 s, cost 0.0764 USD (reported), classifier `jev-1.14.0` calls 7, tokens in 5904, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 63926 / out 716, cost 0.0017 USD (reported), build file judge `jev-1.14.0` calls 8, tokens in 11723, cost 0.0005 USD, phases: path_judge 0.4 s, classifier 0.3 s, context 11.2 s, model_review 417.6 s, config: defaults + host + repository (.werkrevisor.toml at main 9e6dff6).
Reviewed head `5c6a245edc070e6856a1e63d89a2ddf043407548` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
<!-- werkrevisor:5c6a245edc070e6856a1e63d89a2ddf043407548 -->
<!-- werkrevisor-verdict:GREEN -->
<!-- werkrevisor-blocks:90fe99b121c86774ab17d8dc30ca8495,86987efbec7a9183d411aff7919fa36c,a3ce937aa22d01953e0494af0f4be896,b99d6d2400048e361888e4ddf5e87b67,d58a827abd669efc157ba527ff8254e5,deb853f28744d0d6f23a40e3eb3849fd,7b4e23d19ce1717045c7ee0cd4ab5bff,92c6bb18b402621edc76cfbc6a82d948,956f23b6ae6c0e19df957dbd73d80bbb,2d368f08a235eeb850c66b29dfa6a8e0,3ec718b5c0c9aacaff755f84e2a791f6,eba40ddddaa33c1e8e5bd74da341e86f,16b1e83b5b4d7481a45a7e34bd9272b1,0c76ac4965fb6bc41dfbf3f18ce0c7d4,c9261ce9e650f6ac894ecff4b6d9274d,7d8c46ea15cd46cdf90f44e1c71f89b8,fb8c6a0d97a0f1b3bade9e3967f25511,1d06500a43024b014a407a39e12483ae,c28c418cbbafbce9e8b8799322906211 -->
## 🟢 werkrevisor: GREEN / PASS
Change kind: `mixed`.
### Reasons
- **hint** 7 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
- **hint** 1 build change(s) of low risk according to Jev: build.gradle.kts (weakens checks 0.03, risk 0.00, security 0.02)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (each larger than [review] max_full_file_chars = 80000): src/main/resources/templates/releases.html
- **hint** 1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest of its file (each larger than [review] max_full_file_chars = 80000, and no excerpt fits the limits either): src/test/kotlin/de/javagil/werkator/config/ConfigLoaderUnitTest.kt (advisory: Jev judged every changed file)
- **hint** 1 changed file was given to the AI as excerpt of the selected lines and 5 lines around each changed line, not in full (all full files together would exceed [review] max_full_files_chars = 160000): src/test/kotlin/de/javagil/werkator/artifacts/FileArtifactStoreUnitTest.kt
### Classifier
7 of 7 units passed all questions (1 PR document(s)).
2 answers ignored because of a confidence below [classifier] min_confidence.
<sub>
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 57749, calls 1, tokens in 55407 / out 49842 (reasoning included in out), duration 430.0 s, cost 0.0764 USD (reported), classifier `jev-1.14.0` calls 7, tokens in 5904, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 63926 / out 716, cost 0.0017 USD (reported), build file judge `jev-1.14.0` calls 8, tokens in 11723, cost 0.0005 USD, phases: path_judge 0.4 s, classifier 0.3 s, context 11.2 s, model_review 417.6 s, config: defaults + host + repository (.werkrevisor.toml at main 9e6dff6).<br>
Reviewed head `5c6a245edc070e6856a1e63d89a2ddf043407548` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
</sub>
werkrevisor: GREEN / PASS for 5c6a245edc070e6856a1e63d89a2ddf043407548.
The result comment of werkrevisor holds the details.
werkrevisor: GREEN / PASS for `5c6a245edc070e6856a1e63d89a2ddf043407548`.
The result comment of werkrevisor holds the details.
<!-- werkrevisor-review:5c6a245edc070e6856a1e63d89a2ddf043407548 -->
ai-reviewer
removed review request for mi 2026-10-24 20:17:35 +02:00
This changes the security boundary between branch-controlled artifact paths and host filesystem access. A containment mistake could publish host credentials or overwrite files outside the artifact store, so human review is required.
No findings.
The merge waits for the status ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
Model `gpt-6-astra`, effort high, tokens in 201472 (cached 145920) / out 1439, 49.3 s, 11 commands; reviewed head `5c6a245edc070e6856a1e63d89a2ddf043407548`.
<!-- werkrevisor-codex:5c6a245edc070e6856a1e63d89a2ddf043407548 -->
<!-- werkrevisor-codex-critical:true -->
<!-- werkrevisor-codex-unattached:0 -->
<!-- werkrevisor-codex -->
## Codex review: critical
This changes the security boundary between branch-controlled artifact paths and host filesystem access. A containment mistake could publish host credentials or overwrite files outside the artifact store, so human review is required.
No findings.
The merge waits for the status `ai-review/codex`: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.
<sub>
Model `gpt-6-astra`, effort high, tokens in 201472 (cached 145920) / out 1439, 49.3 s, 11 commands; reviewed head `5c6a245edc070e6856a1e63d89a2ddf043407548`.
</sub>
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.
See docs/prs/2026-10-24-PR#167-artifact-dirs-inside-workspace.md
🤖 Generated with Claude Code
🟠 werkrevisor ORANGE for
e4cccbd, superseded by the result for5c6a245🟠 werkrevisor: ORANGE / FINDINGS
Change kind:
mixed.Reasons
Findings
src/main/kotlin/de/javagil/werkator/artifacts/FileArtifactStore.kt:182-189, ORANGE, as a line commentClassifier
7 of 7 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 55291, calls 1, tokens in 53058 / out 48700 (reasoning included in out), duration 439.7 s, cost 0.0743 USD (reported), classifier `jev-1.14.0` calls 7, tokens in 5807, cost 0.0002 USD, finding judge `jev-1.14.0` calls 1, tokens in 2462, cost 0.0001 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 63003 / out 722, cost 0.0082 USD (reported), build file judge `jev-1.14.0` calls 8, tokens in 10963, cost 0.0005 USD, phases: path_judge 0.4 s, classifier 0.3 s, context 12.6 s, model_review 425.6 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main9e6dff6).Reviewed head `e4cccbd23180207905e85569cc7a0b88d3b9a02a` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
werkrevisor: ORANGE / FINDINGS for
e4cccbd23180207905e85569cc7a0b88d3b9a02a.The result comment of werkrevisor holds the details.
@@ -163,0 +179,4 @@if (!Files.isDirectory(source)) {return null}val resolved = source.toRealPath()[high] Symlink Bypass (werkrevisor)
ORANGE; Jev: real 0.81, severity 3.0 of 3
containedSource()validates the resolved directory but returns the unresolvedsource. IfartifactDirsnames a symlink to another directory inside the workspace,Files.walkFileTree(source)visits only that symlink, while the subsequentFiles.copy()follows it and recursively copies the target without checking its descendants. A build can createartifacts -> real-artifactsandreal-artifacts/token -> /home/werkator/.config/token; the root check passes, and the host token is copied into publicly served artifacts. Copy the resolved path through the guarded traversal, or reject symlinked artifact roots, and add this nested case to the tests.Fixed in
5c6a245:containedSourcereturns the checked real path, and the copy walks it, so the link check ofcopyRecursivelycovers everything below an entry that is itself a link.The described leak did not occur before:
walkFileTreedoes not follow a start that is a link and hands it tovisitFile, whereFiles.copycreates only an empty directory, without its entries.But the guarantee hung on that detail, and such an entry stored nothing useful either.
Test:
FileArtifactStoreUnitTest, "an artifact directory that is a link inside the workspace is collected from its target, links below checked"; it fails without the fix.Codex review of
e4cccbd23180207905e85569cc7a0b88d3b9a02a@@ -208,0 +217,4 @@config.buildDefinitions.map { (name, definition) -> "builds.$name" to definition.artifactDirs.orEmpty() }for ((where, entries) in declared) {entries.firstOrNull { !ArtifactDirs.isInsideWorkspace(it) }?.let {throw ConfigFormatException(ArtifactDirs.message(it, where))[medium] Artifact-path rejection during collection leaves the build successful (Codex review)
If a build command changes its workspace’s .werkator.yml to contain artifactDirs: [/home/werkator] and exits successfully, artifact collection reloads that configuration and throws ConfigFormatException here before reaching containedSource. BuildExecutor.storeArtifacts only changes the status for ArtifactEscapeException; its generic catch merely logs this exception. Consequently, nothing is archived but the build and Gitea status remain SUCCESS, contrary to the promised failure for rejected artifact paths. The new store test mocks ConfigLoader, bypassing this actual exception path.
Fix it, or reply why it does not hold, and resolve this comment.
Fixed in
5c6a245:FileArtifactStore.buildSettingsturns aConfigExceptionat collection into anArtifactEscapeException, soBuildExecutor.storeArtifactstakes the build's success back.Test with the real
ConfigLoader:FileArtifactStoreUnitTest, "a configuration the build rewrote to an artifact directory outside its workspace fails the collection"; it fails without the fix.Codex review: critical
This changes the security boundary between branch-controlled artifact paths and host filesystem access. A mistake could expose service credentials through publicly served artifacts, so human review is required.
Findings
src/main/kotlin/de/javagil/werkator/config/ConfigLoader.kt:220, as a line commentThe merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 265649 (cached 203392) / out 2005, 74.7 s, 11 commands; reviewed head `e4cccbd23180207905e85569cc7a0b88d3b9a02a`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.🟢 werkrevisor: GREEN / PASS
Change kind:
mixed.Reasons
Classifier
7 of 7 units passed all questions (1 PR document(s)).
Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 57749, calls 1, tokens in 55407 / out 49842 (reasoning included in out), duration 430.0 s, cost 0.0764 USD (reported), classifier `jev-1.14.0` calls 7, tokens in 5904, cost 0.0002 USD, excerpt selection `openrouter/openai/gpt-6-luna` calls 3, tokens in 63926 / out 716, cost 0.0017 USD (reported), build file judge `jev-1.14.0` calls 8, tokens in 11723, cost 0.0005 USD, phases: path_judge 0.4 s, classifier 0.3 s, context 11.2 s, model_review 417.6 s, config: defaults + host + repository (.werkrevisor.toml at main2 answers ignored because of a confidence below [classifier] min_confidence.
9e6dff6).Reviewed head `5c6a245edc070e6856a1e63d89a2ddf043407548` against base `9e6dff6bbc9553c913a3c091fdea237d6f7aad12`, werkrevisor 0.66.0.
werkrevisor: GREEN / PASS for
5c6a245edc070e6856a1e63d89a2ddf043407548.The result comment of werkrevisor holds the details.
Codex review: critical
This changes the security boundary between branch-controlled artifact paths and host filesystem access. A containment mistake could publish host credentials or overwrite files outside the artifact store, so human review is required.
No findings.
The merge waits for the status
Model `gpt-6-astra`, effort high, tokens in 201472 (cached 145920) / out 1439, 49.3 s, 11 commands; reviewed head `5c6a245edc070e6856a1e63d89a2ddf043407548`.ai-review/codex: every line comment resolved, and the approval of a human for a critical PR or for a finding that is only here.