Skip to content

Commit 94dadf6

Browse files
Record IDR-007: FIO-004 confinement covers the getDataFile surface
Resolves CH-009's blocking open question: the bridge confines paths reached through getDataFile/getDataDirectory, not raw java.io calls that bypass it, matching the architecture record's existing sandbox scoping (threads/reflection/sockets already scoped out). FileAttack is not ported as FIO-004 evidence; FileOutputStreamAttack is. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011GShGLuFwRpnWNY2WL5Vz5
1 parent 9aca007 commit 94dadf6

6 files changed

Lines changed: 42 additions & 19 deletions

File tree

.clue/id-ledger.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ counters:
1010
DES: "8"
1111
EVT: "15"
1212
G: "2"
13-
IDR: "6"
13+
IDR: "7"
1414
OQ: "4"
1515
P: "1"
1616
PDR: "2"
@@ -443,6 +443,11 @@ entries:
443443
state: live
444444
prefix: IDR
445445
component: "6"
446+
- id: IDR-007
447+
kind: numeric
448+
state: reserved
449+
prefix: IDR
450+
component: "7"
446451
- id: OQ-001
447452
kind: numeric
448453
state: live
Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
1-
## Blocking
1+
## Resolved
22

33
### Does `FIO-004` cover raw `java.io` access, or only access reached through `getDataFile`/`getDataDirectory`?
44

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`.
5+
Decided: the resolver-surface reading. See `IDR-007`. `FileOutputStreamAttack` ports as `FIO-004` evidence; `FileAttack` (raw `java.io`, no `getDataFile` call) is not ported under `FIO-004`.

changes/CH-009-m004-file-io-sandboxing/proposal.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
id: CH-009
33
type: change
44
status: proposed
5-
links: [P-001, M-004, CAP-004, C-005, FIO-001, FIO-002, FIO-003, FIO-004, IDR-002]
5+
links: [P-001, M-004, CAP-004, C-005, FIO-001, FIO-002, FIO-003, FIO-004, IDR-002, IDR-007]
66
title: Confine robot file I/O to the robot's data directory
77
---
88

99
# CH-009 — Confine robot file I/O to the robot's data directory
1010

1111
## What
1212

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.
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`, `FIO-004` out of `@draft` with integration evidence, and give `C-005` machine enforcement in place of the agent-judgment note it currently carries.
1414

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.
15+
`FIO-004`'s scope was a blocking open question, resolved as `IDR-007`: confinement covers the `getDataFile`/`getDataDirectory` surface, not raw `java.io` calls that bypass it entirely (classic blocks those with a JVM `SecurityManager` this bridge does not have and the architecture record already scopes out, alongside threads/reflection/sockets).
1616

1717
## Why
1818

changes/CH-009-m004-file-io-sandboxing/tasks.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
- [ ] Resolve the blocking open question on `FIO-004`'s scope before starting its task below.
1+
- [x] Resolve the blocking open question on `FIO-004`'s scope — decided as `IDR-007`: resolver-surface confinement only.
22
- [ ] 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`.
33
- [ ] 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`.
44
- [ ] 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.
5+
- [ ] Port `FileOutputStreamAttack` as conformance evidence for `FIO-001`/`FIO-004` under `ARCH-003`'s tier (`FileAttack` is not ported, per `IDR-007`).
66
- [ ] 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.
7+
- [ ] Remove `@draft` from `FIO-001`, `FIO-002`, `FIO-003`, and `FIO-004` in `CAP-004/criteria.md`, narrowing `FIO-004`'s scenario text to the `getDataFile`-reached surface per `IDR-007`.
88
- [ ] 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).
99
- [ ] 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.
1010
- [ ] 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.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
id: IDR-007
3+
type: decision
4+
status: inferred
5+
author: agent
6+
accepted-by: []
7+
links: [CAP-004, C-005]
8+
title: File I/O confinement covers the getDataFile/getDataDirectory surface, not raw java.io calls
9+
---
10+
11+
# IDR-007 — File I/O confinement covers the `getDataFile`/`getDataDirectory` surface, not raw `java.io` calls
12+
13+
## Decision
14+
15+
`FIO-004` is satisfied by confining paths reached through `getDataFile`/`getDataDirectory` — the surface `RobotData` resolves and `RobocodeFileOutputStream`/`RobocodeFileWriter` route through. A robot that bypasses that surface entirely, opening `java.io.FileInputStream`/`FileOutputStream` on an arbitrary path with no call to `getDataFile`, is not confined by this capability.
16+
17+
## Context
18+
19+
Classic's own conformance robots test both cases. `FileOutputStreamAttack` calls `getDataFile("test")` before opening a stream, so it is confined by construction once `RobotData`'s resolver re-roots the name. `FileAttack` opens `C:\MSDOS.SYS` and `C:\Robocode.attack` directly, with no `getDataFile` call anywhere in the path; classic blocks it with the JVM `SecurityManager`'s `checkRead`/`checkWrite`, a mechanism JDK 24 removed outright and this bridge does not reproduce.
20+
21+
## Why this way
22+
23+
`docs/architecture/README.md` already draws this line: "the rest of classic's sandbox... threads, reflection, and sockets are real gaps that are scoped out rather than forgotten." Raw-`java.io` interception is the same shape of gap — it needs a security-manager successor, a custom `FileSystemProvider`, or bytecode instrumentation of robot jars, none of which exist in this bridge today, and building one is a separately-scoped capability rather than a corner of `CAP-004`'s resolver work. Confining the surface the bridge actually controls, and naming the rest as a known boundary, keeps the claim `CAP-004` makes checkable instead of quietly wider than what was built.
24+
25+
## Consequences
26+
27+
`FileOutputStreamAttack` ports as `FIO-004` evidence; `FileAttack` is not ported under `FIO-004` (it may be recorded elsewhere as a documented out-of-scope gap alongside threads/reflection/sockets, but that is not this decision's obligation to do). A future capability proposing raw-`java.io` confinement would need its own criteria and cannot piggyback on `FIO-004`'s current wording without first revising it.

docs/decisions/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ A decision that changes a methodology contract inventories every live carrier th
3030
- [IDR-004 — EVT-007's cross-engine death-order claim is retired; survivor delivery is measured directly](IDR-004-evt-007-scoped-to-observable-survivor-delivery.md) · `inferred` — Retire `EVT-007` and mint `EVT-014`: a surviving robot receives another robot's death event on each engine.
3131
- [IDR-005 — EVT-001's priority-order claim is retired; the filter boundary is measured directly](IDR-005-evt-001-scoped-to-classic-filter-behavior.md) · `inferred``EVT-001` retires and `EVT-015` measures the observable filter behavior.
3232
- [IDR-006 — Report legacy handler exceptions at the bridge callback boundary](IDR-006-report-legacy-handler-exceptions-at-the-bridge-boundary.md) · `inferred``BotPeer` invokes every legacy robot event callback inside a bridge-owned boundary.
33+
- [IDR-007 — File I/O confinement covers the getDataFile/getDataDirectory surface, not raw java.io calls](IDR-007-file-io-confinement-covers-the-getdatafile-surface.md) · `inferred``FIO-004` is satisfied by confining paths reached through `getDataFile`/`getDataDirectory` — the surface `RobotData` resolves and `RobocodeFileOutputStream`/`RobocodeFileWriter` route through.
3334
<!-- clue:index:end -->

0 commit comments

Comments
 (0)