The sandbox's environment no longer stands on bwrap's command line, nor in a file others can read (0.13.2) #17

Merged
ai-reviewer merged 6 commits from claude-code/bwrap-env-off-argv into main 2026-10-20 18:35:40 +02:00
Collaborator

See docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md.

🤖 Generated with Claude Code

See docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-code added 1 commit 2026-10-20 17:12:51 +02:00
PR document: the sandbox's environment off the bwrap command line
werkator build succeeded after 00:08
werkator/dependencies build succeeded after 00:08
67ada8f60c
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-code added 1 commit 2026-10-20 17:13:51 +02:00
bwrap reads the sandbox's environment from fd 4, not from its command line (0.13.2)
werkator build succeeded after 00:04
werkator/dependencies build succeeded after 00:06
ai-review/assessment human review required; a human approval unlocks the merge; review requested from mi; merge scheduled for when the checks succeed
ai-review/gate build succeeded after 04:28
fa1b5521ef
A value on bwrap's argv stands in /proc/<pid>/cmdline and in every ps;
the values are the secrets of a build. bwrap's own environment is no
alternative: an LD_PRELOAD meant for the sandbox would apply to bwrap
on the host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-code marked the pull request as ready for review 2026-10-20 17:16:53 +02:00
Collaborator
🟡 werkrevisor YELLOW for fa1b552, superseded by the result for afdf9ac

🟡 werkrevisor: YELLOW / HUMAN_REVIEW

Change kind: mixed.

Reasons

  • YELLOW risky paths changed: docs/risk-analysis.md (docs/risk-analysis.md)
  • YELLOW Sandbox isolation according to Jev: internal/engine/bwrap.go (0.90); a human checks it, unless you revert the change because the task does not need it
  • YELLOW spec: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.01 implementation_details (0.63 > yellow_above = 0.6)
  • YELLOW spec: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 hard_to_read (0.55 > yellow_above = 0.5)
  • hint writing rules violated in the changed docs: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:9: line longer than 128 characters (153); docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:23: line longer than 128 characters (147); docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:81: line longer than 128 characters (134)
  • hint 2 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
  • hint 2 finding(s) of low severity or rated minor by Jev: internal/engine/bwrap.go:47-52, internal/engine/bwrap.go:142-144

Findings

  1. [low] Awkward wording in code comment at internal/engine/bwrap.go:47-52, hint, as a line comment

  2. [low] Error message ambiguity at internal/engine/bwrap.go:142-144, hint, as a line comment

Classifier

Unit Question Problem Confidence Verdict
docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.01 implementation_details 0.63 – YELLOW
docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 hard_to_read 0.55 0.59 YELLOW

7 of 9 units passed all questions (1 PR document(s)).
1 answer ignored because of a confidence below [classifier] min_confidence.

Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 30021, calls 1, tokens in 28981 / out 21111 (reasoning included in out), duration 264.9 s, cost 0.0340 USD (reported), classifier `jev-1.13.0` calls 9, tokens in 6872, cost 0.0003 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, build file judge `jev-1.13.0` calls 3, tokens in 5293, cost 0.0002 USD, phases: path_judge 0.3 s, classifier 0.7 s, context 0.2 s, model_review 263.4 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main 03312d4).
Reviewed head `fa1b5521ef3f077d3b6b4539d79bd17374be6095` against base `03312d484ee61a4a64cec000a474a082ce0b6581`, werkrevisor 0.54.1.
<!-- werkrevisor:fa1b5521ef3f077d3b6b4539d79bd17374be6095 --> <!-- werkrevisor-verdict:YELLOW --> <!-- werkrevisor-blocks:cf102e85d5ef9956a37aa698d19af68f,d77dfc29156cf3ab1ae680001a6ffd20,5b4298b16a13e05056792b7de822957a,b9d5a788e932e7af5031f47bb0313b79,e3609263a380d7f5812af57b663d9379,7e857faac973e81e7f419b50f9e92714,38f8c9a83df53e47c05d6204b4f2d823,9fe277402ecfdf2972bdb6bcb6504774,0ae918fabd5cdae037e9b64dda3853f2,46680c9b823ae436f4e8177079058e00,3f032dcfc15bab49c79258805befe3d7,6be94e0c3b0a61b22065c494dbd58b4d,51bef0c97b0221380692d0a5bf191333,de71cb042d0bf6bd66579c0169d23bc1,cc8f40c6622a5545a1e1ac2c6dda7433,73a9159f75162d696e36dafaccb2a63c,48bda00a7630508802be4fde75a60730,5e51e3e31ba0d8e2805f070d4f35537b,fa68d266f5df6dcb039c6fec762a90dc,948ebe38e82becb173ebd2f13a5958ae,57dc07fc962fd5d07541bea8c0591696,1da8b8469ca64a4052c059daf1b46234,1d4eed3af913420dff7e1b5dd01762af,6a0e457324b55cbea4b465267bb41adc,b32a0c192f1bf4ce2b1db65ef40d64b2 --> <!-- werkrevisor-folded --> <details><summary>🟡 werkrevisor YELLOW for <code>fa1b552</code>, superseded by <a href="http://git.javagil.de/mi/werkdock/pulls/17#issuecomment-6757">the result for <code>afdf9ac</code></a></summary> ## 🟡 werkrevisor: YELLOW / HUMAN_REVIEW Change kind: `mixed`. ### Reasons - **YELLOW** risky paths changed: docs/risk-analysis.md (docs/risk-analysis.md) - **YELLOW** Sandbox isolation according to Jev: internal/engine/bwrap.go (0.90); a human checks it, unless you revert the change because the task does not need it - **YELLOW** spec: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.01 implementation_details (0.63 > yellow_above = 0.6) - **YELLOW** spec: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 hard_to_read (0.55 > yellow_above = 0.5) - **hint** writing rules violated in the changed docs: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:9: line longer than 128 characters (153); docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:23: line longer than 128 characters (147); docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:81: line longer than 128 characters (134) - **hint** 2 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev - **hint** 2 finding(s) of low severity or rated minor by Jev: internal/engine/bwrap.go:47-52, internal/engine/bwrap.go:142-144 ### Findings 1. **[low] Awkward wording in code comment** at `internal/engine/bwrap.go:47-52`, hint, as a line comment 2. **[low] Error message ambiguity** at `internal/engine/bwrap.go:142-144`, hint, as a line comment ### Classifier | Unit | Question | Problem | Confidence | Verdict | |---|---|---|---|---| | docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.01 | implementation_details | 0.63 | – | YELLOW | | docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 | hard_to_read | 0.55 | 0.59 | YELLOW | 7 of 9 units passed all questions (1 PR document(s)). 1 answer ignored because of a confidence below [classifier] min_confidence. <sub> Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 30021, calls 1, tokens in 28981 / out 21111 (reasoning included in out), duration 264.9 s, cost 0.0340 USD (reported), classifier `jev-1.13.0` calls 9, tokens in 6872, cost 0.0003 USD, finding judge `none` calls 0, tokens in 0, cost 0.0000 USD, build file judge `jev-1.13.0` calls 3, tokens in 5293, cost 0.0002 USD, phases: path_judge 0.3 s, classifier 0.7 s, context 0.2 s, model_review 263.4 s, finding_judge 0.0 s, config: defaults + host + repository (.werkrevisor.toml at main 03312d4).<br> Reviewed head `fa1b5521ef3f077d3b6b4539d79bd17374be6095` against base `03312d484ee61a4a64cec000a474a082ce0b6581`, werkrevisor 0.54.1. </sub> </details>
ai-reviewer left a comment
Collaborator

werkrevisor: YELLOW / HUMAN_REVIEW for fa1b5521ef3f077d3b6b4539d79bd17374be6095.
The result comment of werkrevisor holds the details.

werkrevisor: YELLOW / HUMAN_REVIEW for `fa1b5521ef3f077d3b6b4539d79bd17374be6095`. The result comment of werkrevisor holds the details. <!-- werkrevisor-review:fa1b5521ef3f077d3b6b4539d79bd17374be6095 -->
@@ -0,0 +6,4 @@
## Related Links
- [werkator PR#166](https://git.javagil.de/mi/werkator/pulls/166): Werkator passes the values of a build's variables to werkdock through its environment.
Collaborator

Writing rule: line longer than 128 characters (153) (werkrevisor writing rule)

Fix the line, or reply why it stays, and resolve this comment.

**Writing rule: line longer than 128 characters (153)** (werkrevisor writing rule) Fix the line, or reply why it stays, and resolve this comment.
Author
Collaborator

Fixed in 38550a0: the link line is wrapped.

Fixed in `38550a0`: the link line is wrapped.
claude-code marked this conversation as resolved
@@ -0,0 +20,4 @@
## Non-Goals
- No change of what the sandbox sees: the image's variables, the `-e` values and werkdock's `HOME` and `PATH`, nothing of the caller's environment.
Collaborator

Writing rule: line longer than 128 characters (147) (werkrevisor writing rule)

Fix the line, or reply why it stays, and resolve this comment.

**Writing rule: line longer than 128 characters (147)** (werkrevisor writing rule) Fix the line, or reply why it stays, and resolve this comment.
Author
Collaborator

Fixed in 38550a0: the Non-Goals line is wrapped.

Fixed in `38550a0`: the Non-Goals line is wrapped.
claude-code marked this conversation as resolved
@@ -0,0 +78,4 @@
## Follow-up PRs
- The configuration of a detached instance (`run -d`, the API socket) holds its environment in a file of mode 0644 below `instances/`.
Collaborator

Writing rule: line longer than 128 characters (134) (werkrevisor writing rule)

Fix the line, or reply why it stays, and resolve this comment.

**Writing rule: line longer than 128 characters (134)** (werkrevisor writing rule) Fix the line, or reply why it stays, and resolve this comment.
Author
Collaborator

Fixed earlier, in afdf9ac: the Follow-up section with that line is gone; no line is longer than 128 characters.

Fixed earlier, in afdf9ac: the Follow-up section with that line is gone; no line is longer than 128 characters.
claude-code marked this conversation as resolved
@@ -44,6 +44,13 @@ const DefaultPATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bi
// as they are in a container without --init. bwrap reports the
// command's host pid on fd 3 (--info-fd), which Start reads.
//
// The environment is cleared here, but set from fd 4 (--args, see
Collaborator

[low] Awkward wording in code comment (werkrevisor)

hint

The new Argv doc comment contains the ungrammatical phrase "bwrap's own environment is no way either", which is hard to parse in context. The sentence is trying to explain that setting the environment through bwrap's own variables is also rejected, because bwrap runs on the host and an LD_PRELOAD meant for the sandbox would load into bwrap itself. A phrasing such as "bwrap's own environment is no better" or "setting it through bwrap's environment is no option either" would convey the intent to a future reader.

**[low] Awkward wording in code comment** (werkrevisor) hint The new Argv doc comment contains the ungrammatical phrase "bwrap's own environment is no way either", which is hard to parse in context. The sentence is trying to explain that setting the environment through bwrap's own variables is also rejected, because bwrap runs on the host and an LD_PRELOAD meant for the sandbox would load into bwrap itself. A phrasing such as "bwrap's own environment is no better" or "setting it through bwrap's environment is no option either" would convey the intent to a future reader.
Author
Collaborator

Fixed in 38550a0: "Setting them as bwrap's own environment is no option either".

Fixed in `38550a0`: "Setting them as bwrap's own environment is no option either".
claude-code marked this conversation as resolved
@@ -127,0 +139,4 @@
}
var args []byte
for _, e := range environment(home, spec.Env) {
if strings.ContainsRune(e.Key, 0) || strings.ContainsRune(e.Value, 0) {
Collaborator

[low] Error message ambiguity (werkrevisor)

hint

When EnvArgs refuses an environment variable for containing a NUL byte, the error message reads "environment variable %q holds a NUL byte" and prints only the key name. If the NUL byte is in the value while the key is well-formed, the message attributes the NUL to the key side and misleads the operator. A realistic scenario: a caller runs -e KEY=val<NUL>tail, gets the error "environment variable "KEY" holds a NUL byte", and looks for the NUL in the key when it is actually in the value.

**[low] Error message ambiguity** (werkrevisor) hint When EnvArgs refuses an environment variable for containing a NUL byte, the error message reads "environment variable %q holds a NUL byte" and prints only the key name. If the NUL byte is in the value while the key is well-formed, the message attributes the NUL to the key side and misleads the operator. A realistic scenario: a caller runs `-e KEY=val<NUL>tail`, gets the error "environment variable \"KEY\" holds a NUL byte", and looks for the NUL in the key when it is actually in the value.
Author
Collaborator

Fixed in 38550a0: the message says "in its name or its value".

Fixed in `38550a0`: the message says "in its name or its value".
claude-code marked this conversation as resolved
ai-reviewer scheduled this pull request to auto merge when all checks succeed 2026-10-20 17:21:34 +02:00
ai-reviewer requested review from mi 2026-10-20 17:21:34 +02:00
claude-code added 1 commit 2026-10-20 17:23:17 +02:00
An instance's directory is 0700, its configuration, state and logs 0600
werkator build succeeded after 00:09
werkator/dependencies build succeeded after 00:06
ai-review/assessment AI findings; a developer approval after checking them unlocks the merge; a human review request is pending; merge already scheduled
ai-review/gate build succeeded after 01:24
afdf9ac1c7
config.json holds the whole environment of the sandbox, also for run
--rm; with 0644 in a 0755 directory, the other accounts of the group
read the secrets of a running build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-code changed title from The sandbox's environment no longer stands on the command line of bwrap (0.13.2) to The sandbox's environment no longer stands on bwrap's command line, nor in a file others can read (0.13.2) 2026-10-20 17:23:18 +02:00
Collaborator
🟠 werkrevisor ORANGE for afdf9ac, superseded by the result for 9402adc

🟠 werkrevisor: ORANGE / FINDINGS

Change kind: mixed.

Reasons

  • ORANGE 2 finding(s) that Jev rates as probably real and severe: internal/engine/bwrap.go:401-404, docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:22-24
  • YELLOW risky paths changed: docs/risk-analysis.md (docs/risk-analysis.md)
  • YELLOW Sandbox isolation according to Jev: internal/engine/bwrap.go (0.91); a human checks it, unless you revert the change because the task does not need it
  • YELLOW spec: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 hard_to_read (0.53 > yellow_above = 0.5)
  • hint writing rules violated in the changed docs: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:9: line longer than 128 characters (153); docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:27: line longer than 128 characters (147)
  • hint 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
  • hint 1 finding(s) of low severity or rated minor by Jev: internal/engine/bwrap.go:112-114
  • hint model recommends changes before merge

Findings

  1. Ignored write error at internal/engine/bwrap.go:401-404, severity unknown, counted as medium, ORANGE, as a line comment

  2. Contradicts the migration story at docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:22-24, severity unknown, counted as medium, ORANGE, as a line comment

  3. EnvArgs called twice at internal/engine/bwrap.go:112-114, severity unknown, counted as medium, hint, as a line comment

Classifier

Unit Question Problem Confidence Verdict
docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 hard_to_read 0.53 0.53 YELLOW

8 of 9 units passed all questions (1 PR document(s)).
1 answer ignored because of a confidence below [classifier] min_confidence.

Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 46731, calls 1, tokens in 45108 / out 6310 (reasoning included in out), duration 80.9 s, cost 0.0211 USD (reported), classifier `jev-1.13.0` calls 9, tokens in 7369, cost 0.0003 USD, finding judge `jev-1.13.0` calls 3, tokens in 5913, cost 0.0002 USD, build file judge `jev-1.13.0` calls 6, tokens in 8400, cost 0.0004 USD, phases: path_judge 0.5 s, classifier 0.6 s, context 0.3 s, model_review 78.8 s, finding_judge 0.3 s, config: defaults + host + repository (.werkrevisor.toml at main 03312d4).
Reviewed head `afdf9ac1c74438907de8861ba0fb0a2457a2d0f2` against base `03312d484ee61a4a64cec000a474a082ce0b6581`, werkrevisor 0.54.1.
<!-- werkrevisor:afdf9ac1c74438907de8861ba0fb0a2457a2d0f2 --> <!-- werkrevisor-verdict:ORANGE --> <!-- werkrevisor-blocks:3b3b4ee3ba528b1e9ee560897b1eed60,425f4c4d3da745c8a0fd40b979256fb6,bad207dfba7581257d047309bc8c74f5,39c0cb52f4a27b5ef5ff246afc2bf801,e3609263a380d7f5812af57b663d9379,7e857faac973e81e7f419b50f9e92714,38f8c9a83df53e47c05d6204b4f2d823,9fe277402ecfdf2972bdb6bcb6504774,0ae918fabd5cdae037e9b64dda3853f2,46680c9b823ae436f4e8177079058e00,3f032dcfc15bab49c79258805befe3d7,6be94e0c3b0a61b22065c494dbd58b4d,51bef0c97b0221380692d0a5bf191333,de71cb042d0bf6bd66579c0169d23bc1,cc8f40c6622a5545a1e1ac2c6dda7433,73a9159f75162d696e36dafaccb2a63c,48bda00a7630508802be4fde75a60730,5e51e3e31ba0d8e2805f070d4f35537b,fa68d266f5df6dcb039c6fec762a90dc,948ebe38e82becb173ebd2f13a5958ae,57dc07fc962fd5d07541bea8c0591696,1da8b8469ca64a4052c059daf1b46234,1d4eed3af913420dff7e1b5dd01762af,6a0e457324b55cbea4b465267bb41adc,b32a0c192f1bf4ce2b1db65ef40d64b2,5c7bbfa26df36a03bdc493eb19b0e63c,d6491cdba43b60bbac87cc150ac87e26,762786a53e02ee2b081f2609f488fdc9,6bff4c7ea6416304e9d54786d894a9ea,feb14579811ef3df0a6af3c5c566242e,c34c3f418b03bbb57c676c1d645231bc --> <!-- werkrevisor-folded --> <details><summary>🟠 werkrevisor ORANGE for <code>afdf9ac</code>, superseded by <a href="http://git.javagil.de/mi/werkdock/pulls/17#issuecomment-6800">the result for <code>9402adc</code></a></summary> ## 🟠 werkrevisor: ORANGE / FINDINGS Change kind: `mixed`. ### Reasons - **ORANGE** 2 finding(s) that Jev rates as probably real and severe: internal/engine/bwrap.go:401-404, docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:22-24 - **YELLOW** risky paths changed: docs/risk-analysis.md (docs/risk-analysis.md) - **YELLOW** Sandbox isolation according to Jev: internal/engine/bwrap.go (0.91); a human checks it, unless you revert the change because the task does not need it - **YELLOW** spec: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 hard_to_read (0.53 > yellow_above = 0.5) - **hint** writing rules violated in the changed docs: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:9: line longer than 128 characters (153); docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:27: line longer than 128 characters (147) - **hint** 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev - **hint** 1 finding(s) of low severity or rated minor by Jev: internal/engine/bwrap.go:112-114 - **hint** model recommends changes before merge ### Findings 1. **Ignored write error** at `internal/engine/bwrap.go:401-404`, severity unknown, counted as medium, ORANGE, as a line comment 2. **Contradicts the migration story** at `docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md:22-24`, severity unknown, counted as medium, ORANGE, as a line comment 3. **EnvArgs called twice** at `internal/engine/bwrap.go:112-114`, severity unknown, counted as medium, hint, as a line comment ### Classifier | Unit | Question | Problem | Confidence | Verdict | |---|---|---|---|---| | docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 | hard_to_read | 0.53 | 0.53 | YELLOW | 8 of 9 units passed all questions (1 PR document(s)). 1 answer ignored because of a confidence below [classifier] min_confidence. <sub> Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 46731, calls 1, tokens in 45108 / out 6310 (reasoning included in out), duration 80.9 s, cost 0.0211 USD (reported), classifier `jev-1.13.0` calls 9, tokens in 7369, cost 0.0003 USD, finding judge `jev-1.13.0` calls 3, tokens in 5913, cost 0.0002 USD, build file judge `jev-1.13.0` calls 6, tokens in 8400, cost 0.0004 USD, phases: path_judge 0.5 s, classifier 0.6 s, context 0.3 s, model_review 78.8 s, finding_judge 0.3 s, config: defaults + host + repository (.werkrevisor.toml at main 03312d4).<br> Reviewed head `afdf9ac1c74438907de8861ba0fb0a2457a2d0f2` against base `03312d484ee61a4a64cec000a474a082ce0b6581`, werkrevisor 0.54.1. </sub> </details>
ai-reviewer left a comment
Collaborator

werkrevisor: ORANGE / FINDINGS for afdf9ac1c74438907de8861ba0fb0a2457a2d0f2.
The result comment of werkrevisor holds the details.

werkrevisor: ORANGE / FINDINGS for `afdf9ac1c74438907de8861ba0fb0a2457a2d0f2`. The result comment of werkrevisor holds the details. <!-- werkrevisor-review:afdf9ac1c74438907de8861ba0fb0a2457a2d0f2 -->
@@ -0,0 +19,4 @@
bwrap's argv is the last place where they stand.
A second leak, found on mih09 the same day: each instance, also of an attached `run --rm`,
writes its configuration with the whole environment to `instances/<id>/config.json`, mode 0644, in a directory of mode 0755.
Collaborator

Contradicts the migration story, severity unknown, counted as medium (werkrevisor)

ORANGE; Jev: real 0.79, severity 3.0 of 3

Section "For consumers" in CHANGELOG.md tells consumers to "Pass a secret as a bare -e KEY with the value in werkdock's own environment, as Werkator does since werkator PR#166". The PR document's Solution section says the secrets end up in instances/<id>/config.json and describes tightening its mode, but never addresses that this very file still serializes the full environment to disk on run --rm and run -d. The filesystem-permission change closes the world-readable hole, but config.json still holds every secret in plaintext at rest — a fact the PR document should acknowledge alongside the mode tightening rather than implicitly claiming the secrets are merely off the command line.

**Contradicts the migration story**, severity unknown, counted as medium (werkrevisor) ORANGE; Jev: real 0.79, severity 3.0 of 3 Section "For consumers" in CHANGELOG.md tells consumers to "Pass a secret as a bare `-e KEY` with the value in werkdock's own environment, as Werkator does since werkator PR#166". The PR document's Solution section says the secrets end up in `instances/<id>/config.json` and describes tightening its mode, but never addresses that this very file still serializes the full environment to disk on `run --rm` and `run -d`. The filesystem-permission change closes the world-readable hole, but config.json still holds every secret in plaintext at rest — a fact the PR document should acknowledge alongside the mode tightening rather than implicitly claiming the secrets are merely off the command line.
Author
Collaborator

Fixed in 38550a0: the Non-Goals say that config.json keeps the environment in plain text, why (start of a stopped instance needs it), and that mode 0600 limits it to the account, which holds the secrets anyway.

Fixed in `38550a0`: the Non-Goals say that config.json keeps the environment in plain text, why (`start` of a stopped instance needs it), and that mode 0600 limits it to the account, which holds the secrets anyway.
claude-code marked this conversation as resolved
@@ -109,3 +112,1 @@
args = append(args, "--clearenv")
for _, e := range environment(home, spec.Env) {
args = append(args, "--setenv", e.Key, e.Value)
if _, err := EnvArgs(spec); err != nil {
Collaborator

EnvArgs called twice, severity unknown, counted as medium (werkrevisor)

hint; Jev: real 0.70, severity 1.9 of 3

Argv calls EnvArgs(spec) to validate (lines 112-114) and discards the returned byte slice, then Start (lines 365-367) calls EnvArgs(spec) again to get the actual payload. Iterating environment(home, spec.Env) and checking NUL bytes runs twice per sandbox for no reason. Either have Argv return the payload, or pass through the already-validated slice; otherwise the work is duplicated for every sandbox start.

**EnvArgs called twice**, severity unknown, counted as medium (werkrevisor) hint; Jev: real 0.70, severity 1.9 of 3 `Argv` calls `EnvArgs(spec)` to validate (lines 112-114) and discards the returned byte slice, then `Start` (lines 365-367) calls `EnvArgs(spec)` again to get the actual payload. Iterating `environment(home, spec.Env)` and checking NUL bytes runs twice per sandbox for no reason. Either have `Argv` return the payload, or pass through the already-validated slice; otherwise the work is duplicated for every sandbox start.
Author
Collaborator

Stays, on purpose: Argv is also what werkdock run validates a spec with before it creates the instance (internal/cli/run.go:108), so it must refuse a NUL byte as Start would. A second pass over a few variables costs microseconds per sandbox start.

Stays, on purpose: `Argv` is also what `werkdock run` validates a spec with before it creates the instance (internal/cli/run.go:108), so it must refuse a NUL byte as `Start` would. A second pass over a few variables costs microseconds per sandbox start.
claude-code marked this conversation as resolved
@@ -362,0 +398,4 @@
envR.Close()
// written while bwrap reads, since the environment may exceed what
// a pipe buffers; bwrap reads it before anything else it does
go func() {
Collaborator

Ignored write error, severity unknown, counted as medium (werkrevisor)

ORANGE; Jev: real 0.81, severity 2.9 of 3

In Start, the goroutine writes the environment payload to the pipe with envW.Write(env); envW.Close() and ignores the returned error. While Go's (*os.File).Write is documented to return a non-nil error when n != len(b) (so a silent truncation on a single call is rare), a write error here signals that bwrap cannot or will not receive the full environment — leaving the sandbox with missing variables and no surfaced failure. With a NUL-framed protocol this is also a correctness issue: a partial write that returns an error drops trailing variables rather than producing a malformed frame, but the supervisor proceeds as if Start succeeded. Loop until all bytes are written or handle the error.

**Ignored write error**, severity unknown, counted as medium (werkrevisor) ORANGE; Jev: real 0.81, severity 2.9 of 3 In `Start`, the goroutine writes the environment payload to the pipe with `envW.Write(env); envW.Close()` and ignores the returned error. While Go's `(*os.File).Write` is documented to return a non-nil error when n != len(b) (so a silent truncation on a single call is rare), a write error here signals that bwrap cannot or will not receive the full environment — leaving the sandbox with missing variables and no surfaced failure. With a NUL-framed protocol this is also a correctness issue: a partial write that returns an error drops trailing variables rather than producing a malformed frame, but the supervisor proceeds as if Start succeeded. Loop until all bytes are written or handle the error.
Author
Collaborator

Fixed in 38550a0: a failed write kills bwrap before the pipe closes. bwrap reads fd 4 up to EOF before it does anything else, so it never starts a sandbox with part of its environment; Start then fails on the missing child pid.

Fixed in `38550a0`: a failed write kills bwrap before the pipe closes. bwrap reads fd 4 up to EOF before it does anything else, so it never starts a sandbox with part of its environment; Start then fails on the missing child pid.
claude-code marked this conversation as resolved
claude-code added 1 commit 2026-10-20 17:28:36 +02:00
Review: a failed write of the environment kills bwrap; wording, error message, line lengths
werkator build succeeded after 00:08
werkator/dependencies build succeeded after 00:06
ai-review/gate build failed after 02:56
38550a0f43
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-code added 1 commit 2026-10-20 17:30:19 +02:00
Review: Scenario#17.02 in plainer words
werkator build succeeded after 00:05
werkator/dependencies build succeeded after 00:10
ai-review/assessment AI findings; a developer approval after checking them unlocks the merge; a human review request is pending; merge already scheduled
ai-review/gate build succeeded after 05:45
9402adc0f9
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
🟠 werkrevisor ORANGE for 9402adc, superseded by the result for 1860d07

🟠 werkrevisor: ORANGE / FINDINGS

Change kind: mixed.

Reasons

  • ORANGE 1 finding(s) that Jev rates as probably real and severe: internal/engine/bwrap.go:403-408
  • YELLOW risky paths changed: docs/risk-analysis.md (docs/risk-analysis.md)
  • YELLOW Sandbox isolation according to Jev: internal/engine/bwrap.go (0.89); a human checks it, unless you revert the change because the task does not need it
  • YELLOW spec: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 hard_to_read (0.52 > yellow_above = 0.5)
  • hint 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev
  • hint model recommends merge with caution

Findings

  1. SIGPIPE Risk at internal/engine/bwrap.go:403-408, severity unknown, counted as medium, ORANGE, as a line comment

Classifier

Unit Question Problem Confidence Verdict
docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 hard_to_read 0.52 0.50 YELLOW

8 of 9 units passed all questions (1 PR document(s)).
1 answer ignored because of a confidence below [classifier] min_confidence.

Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 46920, calls 1, tokens in 45286 / out 28445 (reasoning included in out), duration 342.4 s, cost 0.0468 USD (reported), classifier `jev-1.13.0` calls 9, tokens in 7419, cost 0.0003 USD, finding judge `jev-1.13.0` calls 1, tokens in 1985, cost 0.0001 USD, build file judge `jev-1.13.0` calls 6, tokens in 8460, cost 0.0004 USD, phases: path_judge 0.4 s, classifier 0.6 s, context 0.3 s, model_review 340.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 03312d4).
Reviewed head `9402adc0f93126f6ba6d914ac59932240ebc820e` against base `03312d484ee61a4a64cec000a474a082ce0b6581`, werkrevisor 0.54.1.
<!-- werkrevisor:9402adc0f93126f6ba6d914ac59932240ebc820e --> <!-- werkrevisor-verdict:ORANGE --> <!-- werkrevisor-blocks:3b3b4ee3ba528b1e9ee560897b1eed60,e7297cbf3bbb04bf7b8c1abea128d8fa,bad207dfba7581257d047309bc8c74f5,39c0cb52f4a27b5ef5ff246afc2bf801,e3609263a380d7f5812af57b663d9379,61dbd255093aa446ae5571b265e8310e,38f8c9a83df53e47c05d6204b4f2d823,9fe277402ecfdf2972bdb6bcb6504774,c6f8e5534d7a2efeb4c7475aac8b2352,46680c9b823ae436f4e8177079058e00,3f032dcfc15bab49c79258805befe3d7,6be94e0c3b0a61b22065c494dbd58b4d,51bef0c97b0221380692d0a5bf191333,d7cac39f4a80835f9522c439e4f3025b,cc8f40c6622a5545a1e1ac2c6dda7433,73a9159f75162d696e36dafaccb2a63c,48bda00a7630508802be4fde75a60730,5e51e3e31ba0d8e2805f070d4f35537b,fa68d266f5df6dcb039c6fec762a90dc,948ebe38e82becb173ebd2f13a5958ae,57dc07fc962fd5d07541bea8c0591696,1da8b8469ca64a4052c059daf1b46234,1d4eed3af913420dff7e1b5dd01762af,6a0e457324b55cbea4b465267bb41adc,b32a0c192f1bf4ce2b1db65ef40d64b2,5c7bbfa26df36a03bdc493eb19b0e63c,d6491cdba43b60bbac87cc150ac87e26,762786a53e02ee2b081f2609f488fdc9,6bff4c7ea6416304e9d54786d894a9ea,feb14579811ef3df0a6af3c5c566242e,c34c3f418b03bbb57c676c1d645231bc --> <!-- werkrevisor-folded --> <details><summary>🟠 werkrevisor ORANGE for <code>9402adc</code>, superseded by <a href="http://git.javagil.de/mi/werkdock/pulls/17#issuecomment-6815">the result for <code>1860d07</code></a></summary> ## 🟠 werkrevisor: ORANGE / FINDINGS Change kind: `mixed`. ### Reasons - **ORANGE** 1 finding(s) that Jev rates as probably real and severe: internal/engine/bwrap.go:403-408 - **YELLOW** risky paths changed: docs/risk-analysis.md (docs/risk-analysis.md) - **YELLOW** Sandbox isolation according to Jev: internal/engine/bwrap.go (0.89); a human checks it, unless you revert the change because the task does not need it - **YELLOW** spec: docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 hard_to_read (0.52 > yellow_above = 0.5) - **hint** 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev - **hint** model recommends merge with caution ### Findings 1. **SIGPIPE Risk** at `internal/engine/bwrap.go:403-408`, severity unknown, counted as medium, ORANGE, as a line comment ### Classifier | Unit | Question | Problem | Confidence | Verdict | |---|---|---|---|---| | docs/prs/2026-10-20-PR#17-bwrap-env-off-argv.md Scenario#17.02 | hard_to_read | 0.52 | 0.50 | YELLOW | 8 of 9 units passed all questions (1 PR document(s)). 1 answer ignored because of a confidence below [classifier] min_confidence. <sub> Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 46920, calls 1, tokens in 45286 / out 28445 (reasoning included in out), duration 342.4 s, cost 0.0468 USD (reported), classifier `jev-1.13.0` calls 9, tokens in 7419, cost 0.0003 USD, finding judge `jev-1.13.0` calls 1, tokens in 1985, cost 0.0001 USD, build file judge `jev-1.13.0` calls 6, tokens in 8460, cost 0.0004 USD, phases: path_judge 0.4 s, classifier 0.6 s, context 0.3 s, model_review 340.3 s, finding_judge 0.2 s, config: defaults + host + repository (.werkrevisor.toml at main 03312d4).<br> Reviewed head `9402adc0f93126f6ba6d914ac59932240ebc820e` against base `03312d484ee61a4a64cec000a474a082ce0b6581`, werkrevisor 0.54.1. </sub> </details>
ai-reviewer left a comment
Collaborator

werkrevisor: ORANGE / FINDINGS for 9402adc0f93126f6ba6d914ac59932240ebc820e.
The result comment of werkrevisor holds the details.

werkrevisor: ORANGE / FINDINGS for `9402adc0f93126f6ba6d914ac59932240ebc820e`. The result comment of werkrevisor holds the details. <!-- werkrevisor-review:9402adc0f93126f6ba6d914ac59932240ebc820e -->
@@ -362,0 +400,4 @@
// a pipe buffers; bwrap reads it up to EOF before anything else it
// does, so a sandbox that would get only part of it is killed before
// the close could hand it that part
go func() {
Collaborator

SIGPIPE Risk, severity unknown, counted as medium (werkrevisor)

ORANGE; Jev: real 0.76, severity 3.0 of 3

The goroutine writes the sandbox's environment to envW without ignoring SIGPIPE. If bwrap dies before or while reading fd 4 — a too-large value rejected by execve, an internal bwrap crash, or any external kill of bwrap — the subsequent envW.Write returns EPIPE and SIGPIPE terminates the werkdock process before the goroutine can run cmd.Process.Kill(). The go runtime only ignores SIGPIPE for net.Conn reads/writes; raw os.File writes do not have this protection. Add signal.Ignore(syscall.SIGPIPE) (at process startup or around the write) so EPIPE is handled in Go rather than killing werkdock and orphaning bwrap.

**SIGPIPE Risk**, severity unknown, counted as medium (werkrevisor) ORANGE; Jev: real 0.76, severity 3.0 of 3 The goroutine writes the sandbox's environment to `envW` without ignoring SIGPIPE. If bwrap dies before or while reading fd 4 — a too-large value rejected by execve, an internal bwrap crash, or any external kill of bwrap — the subsequent `envW.Write` returns EPIPE and SIGPIPE terminates the werkdock process before the goroutine can run `cmd.Process.Kill()`. The go runtime only ignores SIGPIPE for `net.Conn` reads/writes; raw `os.File` writes do not have this protection. Add `signal.Ignore(syscall.SIGPIPE)` (at process startup or around the write) so EPIPE is handled in Go rather than killing werkdock and orphaning bwrap.
Author
Collaborator

No change; the finding does not hold for Go. The os/signal documentation, section "SIGPIPE": a write to a broken pipe on file descriptors 1 or 2 makes the program exit with SIGPIPE; on any other file descriptor the runtime takes no action on the signal, and the write fails with EPIPE. That holds for os.File, not only net.Conn. The env pipe is a fresh descriptor, never 1 or 2. Checked with go1.26.10: a program that writes to an os.Pipe whose read end is closed prints "write |1: broken pipe" and exits 0. So the Write returns the error, and the goroutine kills bwrap as intended.

No change; the finding does not hold for Go. The os/signal documentation, section "SIGPIPE": a write to a broken pipe on file descriptors 1 or 2 makes the program exit with SIGPIPE; on any other file descriptor the runtime takes no action on the signal, and the write fails with EPIPE. That holds for os.File, not only net.Conn. The env pipe is a fresh descriptor, never 1 or 2. Checked with go1.26.10: a program that writes to an os.Pipe whose read end is closed prints "write |1: broken pipe" and exits 0. So the Write returns the error, and the goroutine kills bwrap as intended.
claude-code marked this conversation as resolved
claude-code added 1 commit 2026-10-20 17:42:39 +02:00
Review: rerun after the replies on SIGPIPE
werkator build succeeded after 00:05
werkator/dependencies build succeeded after 00:08
ai-review/assessment human review required; a human approval unlocks the merge; a human review request is pending; merge already scheduled
ai-review/gate build succeeded after 02:08
1860d076fb
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator

🟡 werkrevisor: YELLOW / HUMAN_REVIEW

Change kind: mixed.

Reasons

  • YELLOW risky paths changed: docs/risk-analysis.md (docs/risk-analysis.md)
  • YELLOW Sandbox isolation according to Jev: internal/engine/bwrap.go (0.90); a human checks it, unless you revert the change because the task does not need it
  • hint 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev

Classifier

9 of 9 units passed all questions (1 PR document(s)).
1 answer ignored because of a confidence below [classifier] min_confidence.

Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 46875, calls 1, tokens in 45244 / out 10306 (reasoning included in out), duration 125.6 s, cost 0.0259 USD (reported), classifier `jev-1.13.0` calls 9, tokens in 7419, cost 0.0003 USD, build file judge `jev-1.13.0` calls 6, tokens in 8460, cost 0.0004 USD, phases: path_judge 0.5 s, classifier 0.6 s, context 0.3 s, model_review 123.7 s, config: defaults + host + repository (.werkrevisor.toml at main 03312d4).
Reviewed head `1860d076fbf25f2d48ba4f5785e37144366b4ec1` against base `03312d484ee61a4a64cec000a474a082ce0b6581`, werkrevisor 0.54.1.
<!-- werkrevisor:1860d076fbf25f2d48ba4f5785e37144366b4ec1 --> <!-- werkrevisor-verdict:YELLOW --> <!-- werkrevisor-blocks:3b3b4ee3ba528b1e9ee560897b1eed60,e7297cbf3bbb04bf7b8c1abea128d8fa,bad207dfba7581257d047309bc8c74f5,39c0cb52f4a27b5ef5ff246afc2bf801,e3609263a380d7f5812af57b663d9379,61dbd255093aa446ae5571b265e8310e,38f8c9a83df53e47c05d6204b4f2d823,9fe277402ecfdf2972bdb6bcb6504774,c6f8e5534d7a2efeb4c7475aac8b2352,46680c9b823ae436f4e8177079058e00,3f032dcfc15bab49c79258805befe3d7,6be94e0c3b0a61b22065c494dbd58b4d,51bef0c97b0221380692d0a5bf191333,d7cac39f4a80835f9522c439e4f3025b,cc8f40c6622a5545a1e1ac2c6dda7433,73a9159f75162d696e36dafaccb2a63c,48bda00a7630508802be4fde75a60730,5e51e3e31ba0d8e2805f070d4f35537b,fa68d266f5df6dcb039c6fec762a90dc,948ebe38e82becb173ebd2f13a5958ae,57dc07fc962fd5d07541bea8c0591696,1da8b8469ca64a4052c059daf1b46234,1d4eed3af913420dff7e1b5dd01762af,6a0e457324b55cbea4b465267bb41adc,b32a0c192f1bf4ce2b1db65ef40d64b2,5c7bbfa26df36a03bdc493eb19b0e63c,d6491cdba43b60bbac87cc150ac87e26,762786a53e02ee2b081f2609f488fdc9,6bff4c7ea6416304e9d54786d894a9ea,feb14579811ef3df0a6af3c5c566242e,c34c3f418b03bbb57c676c1d645231bc --> ## 🟡 werkrevisor: YELLOW / HUMAN_REVIEW Change kind: `mixed`. ### Reasons - **YELLOW** risky paths changed: docs/risk-analysis.md (docs/risk-analysis.md) - **YELLOW** Sandbox isolation according to Jev: internal/engine/bwrap.go (0.90); a human checks it, unless you revert the change because the task does not need it - **hint** 5 changed file(s) that neither weaken the checks, touch security nor show a risk category of jvm-spring-backend according to Jev ### Classifier 9 of 9 units passed all questions (1 PR document(s)). 1 answer ignored because of a confidence below [classifier] min_confidence. <sub> Usage: model `openrouter/minimax/minimax-m3`, LLM done, tokens counted before the call 46875, calls 1, tokens in 45244 / out 10306 (reasoning included in out), duration 125.6 s, cost 0.0259 USD (reported), classifier `jev-1.13.0` calls 9, tokens in 7419, cost 0.0003 USD, build file judge `jev-1.13.0` calls 6, tokens in 8460, cost 0.0004 USD, phases: path_judge 0.5 s, classifier 0.6 s, context 0.3 s, model_review 123.7 s, config: defaults + host + repository (.werkrevisor.toml at main 03312d4).<br> Reviewed head `1860d076fbf25f2d48ba4f5785e37144366b4ec1` against base `03312d484ee61a4a64cec000a474a082ce0b6581`, werkrevisor 0.54.1. </sub>
mi approved these changes 2026-10-20 18:35:37 +02:00
ai-reviewer merged commit b2266658f3 into main 2026-10-20 18:35:40 +02:00
ai-reviewer deleted branch claude-code/bwrap-env-off-argv 2026-10-20 18:35:40 +02:00
Sign in to join this conversation.
No Reviewers
No labels
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mi/werkdock#17