Skip to content

Commit 9aca007

Browse files
Propose CH-009: confine robot file I/O to the data directory
Scopes M-004's fix at RobotData's single resolution point (re-root absolute/traversing paths, enforce the documented 200000-byte quota), closing FIO-001..003. Flags FIO-004's raw java.io scope as a blocking open question against the architecture record's explicit sandbox descoping. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011GShGLuFwRpnWNY2WL5Vz5
1 parent 78078fc commit 9aca007

4 files changed

Lines changed: 57 additions & 1 deletion

File tree

.clue/id-ledger.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ counters:
55
ARCH: "3"
66
C: "7"
77
CAP: "8"
8-
CH: "8"
8+
CH: "9"
99
CRIT: "8"
1010
DES: "8"
1111
EVT: "15"
@@ -243,6 +243,11 @@ entries:
243243
state: reserved
244244
prefix: CH
245245
component: "8"
246+
- id: CH-009
247+
kind: numeric
248+
state: reserved
249+
prefix: CH
250+
component: "9"
246251
- id: CRIT-001
247252
kind: numeric
248253
state: live
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## Blocking
2+
3+
### Does `FIO-004` cover raw `java.io` access, or only access reached through `getDataFile`/`getDataDirectory`?
4+
5+
`CAP-004`'s criteria name the ported classic test robots `FileAttack` and `FileOutputStreamAttack` as `FIO-004`'s evidence plan. Reading them in `C:/Code/robocode/robocode.tests.robots/src/main/java/tested/robots/`:
6+
7+
- `FileOutputStreamAttack` calls `getDataFile("test")`, then opens a plain `java.io.FileOutputStream` on the returned `File`. This is exactly the path this change's resolver covers: `getDataFile` re-roots the name, and whatever opens the resulting `File` sees an already-confined path. Porting this robot needs no scope decision.
8+
- `FileAttack` never calls `getDataFile` at all. It opens `new FileInputStream("C:\\MSDOS.SYS")` and `new FileOutputStream("C:\\Robocode.attack")` directly. In classic, this is blocked by the JVM `SecurityManager`'s `checkRead`/`checkWrite` — a mechanism this repository's own architecture record says is out of scope: `docs/architecture/README.md` lists "threads, reflection, and sockets" as sandbox gaps "scoped out rather than forgotten," and `AGENTS.md` records that JDK 24 removed `SecurityManager` outright, which is why classic needs its own older JDK to run at all. A resolver inside `getDataFile` cannot see or redirect a call that never goes through it.
9+
10+
Two ways to resolve this, neither of which this change should choose on its own:
11+
12+
1. **Narrow `FIO-004` to the `getDataFile`-reached surface.** Port `FileOutputStreamAttack` as `FIO-004`'s evidence; drop `FileAttack` or retarget it at a different criterion (or a documented, deliberately out-of-scope gap alongside threads/reflection/sockets). This keeps `CAP-004` inside the boundary the architecture record already drew.
13+
2. **Widen scope to intercept raw `java.io` calls from robot code.** This would need a mechanism the bridge does not have today — a security-manager successor (Java's replacement APIs), a custom `FileSystemProvider`, or bytecode instrumentation of robot jars at wrap time — and is a materially larger and separately-plannable piece of work, arguably its own capability rather than a corner of `CAP-004`.
14+
15+
This change proceeds on option 1's assumption (resolver-surface confinement only, `FileAttack` excluded or retargeted) unless directed otherwise, because option 2 is a different-shaped change that this plan door's own scope note (`docs/architecture/README.md`'s "scoped out rather than forgotten") argues against undertaking implicitly. Flagging here rather than deciding silently, per the scope-change rule in `AGENTS.md`.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
id: CH-009
3+
type: change
4+
status: proposed
5+
links: [P-001, M-004, CAP-004, C-005, FIO-001, FIO-002, FIO-003, FIO-004, IDR-002]
6+
title: Confine robot file I/O to the robot's data directory
7+
---
8+
9+
# CH-009 — Confine robot file I/O to the robot's data directory
10+
11+
## What
12+
13+
Give `RobotData.getDataFile`/`getDataDirectory` — the one resolution point `robocode.RobocodeFileOutputStream` and `robocode.RobocodeFileWriter` already route through — the confinement classic Robocode has and the bridge does not: an absolute or traversing path a robot names is re-rooted inside the robot's data directory rather than passed through, and the directory's total size is capped at the documented 200000-byte quota. Move `FIO-001`, `FIO-002`, `FIO-003` out of `@draft` with integration evidence, and give `C-005` machine enforcement in place of the agent-judgment note it currently carries.
14+
15+
`FIO-004`'s scope is a blocking open question (see `open-questions.md`) and is not committed to by this proposal until it is answered.
16+
17+
## Why
18+
19+
`C-005` already states the defect: `RobotData.getDataFile` uses `java.nio.file.Path#resolve`, which returns an absolute argument unchanged rather than re-rooting it — the opposite of classic's silent redirection. A rumble bot that writes to a root-level path (`AN-012`'s note on Windows permission-failure floods) produces thousands of access-denied errors under the bridge where classic produced a normal write inside the robot's directory. `IDR-002` aligned `getDataDirectory` and `getDataFile` to the same lookup but explicitly left confinement undone; `FIO-002`'s `@draft` status is the record of that gap, and this change is what closes it.
20+
21+
The quota (`FIO-003`) is documented in three Javadoc comments (`RobocodeFileOutputStream`, `RobocodeFileWriter`, `IAdvancedRobotPeer#getDataFile`) and enforced nowhere; a robot that reads its own Javadoc and expects a `SYSTEM:`-prefixed quota message and an `IOException` at 200000 bytes currently gets neither.
22+
23+
## Route
24+
25+
Full. This closes acceptance criteria that are currently `@draft` (`FIO-001`, `FIO-002`, `FIO-003`) and promotes `C-005` from `enforcement: agent` to machine-enforced — both are contract changes, not refactors of unchanged behaviour.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
- [ ] Resolve the blocking open question on `FIO-004`'s scope before starting its task below.
2+
- [ ] Implement the single resolution point in `RobotData`: re-root an absolute or drive-rooted `filename` argument inside the data directory, resolve `..` traversal inside the directory rather than escaping it, and keep `getDataDirectory`/`getDataFile` agreeing as `IDR-002` already requires — serves `FIO-001`, `FIO-002`.
3+
- [ ] Add the 200000-byte data-directory quota at the same resolution point (tracking bytes written across the directory's lifetime, refusing further writes with the same `IOException` classic raises) — serves `FIO-003`.
4+
- [ ] Port classic's `TestFileWriteSize` conformance robot/test (`C:/Code/robocode/robocode.tests.robots/.../FileWriteSize.java` and its test) for `FIO-003`'s quota-exceeded evidence, and a companion positive case that stays under quota.
5+
- [ ] Port `FileOutputStreamAttack` (and, depending on the open question's answer, `FileAttack` or a retargeted equivalent) as conformance evidence for `FIO-001`/`FIO-004` under `ARCH-003`'s tier.
6+
- [ ] Add a focused unit case for `FIO-002`: writing through `getDataFile(name)` and listing `getDataDirectory()` see the same file, for both a plain name and a re-rooted absolute/traversing one.
7+
- [ ] Remove `@draft` from `FIO-001`, `FIO-002`, `FIO-003` in `CAP-004/criteria.md`; leave `FIO-004` `@draft` if its scope is narrowed to a criterion this change does not fully close, or resolve it if the answer lets it close here.
8+
- [ ] Update `CAP-004/design.md` and `CAP-004/README.md` to describe the resolver as implemented rather than intended, and update `C-005` from `enforcement: agent` to its machine-enforced state (or promote-with-residual note if `FIO-004` stays partly open).
9+
- [ ] Update `P-001`'s evidence-door table (`M-119``M-122` equivalents don't exist for `FIO-00x`; confirm the milestone-status row for `M-004` and any doors naming `FIO-001`..`FIO-004`) to reflect what closed.
10+
- [ ] Run the tier-1 unit suite and the focused tier-2 conformance tests with the matched local Tank Royale API/runner pair; retain any environment limitation in the handoff.
11+
- [ ] Digest: regenerate indexes, run `clue validate`, commit the final state, and delete this change workspace.

0 commit comments

Comments
 (0)