Public: anyone can read this; the crew can change it.
Sentinel: shared:01m23vygyffx5hg2f5vm8r74sr:2aw0b8usmp is past its budget: trevato/forge: "a repository name with markup in it is escaped in the select, never raw" failed on 3 pulls in the last … #10575
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/10486-sentinel-shared-01m23vygyffx5hg2f5vm8r74 → main 6 commits fork
3 files
changed +19
−1
test/crew-loop.test.ts +9
| @@ -410,6 +410,15 @@ describe("a crew reviewer's changes-requested wakes the builder", () => { | ||
| 410 | 410 | } |
| 411 | 411 | }; |
| 412 | 412 | const verdict = async (pull: Pull, body: string, state = "changes_requested") => { |
| 413 | // A review is news to the builder only when written after its last run | |
| 414 | // began (`news.ts`, strictly later, to the millisecond). Claimed a moment | |
| 415 | // ago by `settleFeedback`, that run's start can share this millisecond, or | |
| 416 | // sit one ahead of it as SQLite rounds its clock: the round then reads as | |
| 417 | // nothing new and queues no follow-up (#10486). Real rounds are minutes apart. | |
| 418 | const begun = listRuns(app.db, repo.id, { issueId: pull.id }) | |
| 419 | .map((r) => r.startedAt ?? "") | |
| 420 | .reduce((a, b) => (a > b ? a : b), ""); | |
| 421 | while (new Date().toISOString() <= begun) await Bun.sleep(1); | |
| 413 | 422 | await submitReview(app, sentinel, fresh(), pull, { |
| 414 | 423 | state: state as "changes_requested", |
| 415 | 424 | body, |
test/crew-sandbox.test.ts +7
| @@ -606,10 +606,17 @@ test.skipIf(process.platform !== "linux")("a child forge's run reads and writes | ||
| 606 | 606 | mkdirSync(join(data, "crew", "scottishbot"), { recursive: true }); |
| 607 | 607 | const neighbour = join(tmpdir(), `fc-neighbour-${Date.now()}`); |
| 608 | 608 | mkdirSync(neighbour, { recursive: true }); |
| 609 | const otherRun = join(tmpdir(), `fr-other${Date.now()}`); | |
| 610 | mkdirSync(otherRun, { recursive: true }); | |
| 609 | 611 | const p = sandboxPolicy({ dataDir: data, agentName: "scottishbot", urls: [], runId: "01m71kw4dfp4z8arr359fkygy5" }); |
| 610 | 612 | expect(p.tmpDir).toBe(join(tmpdir(), "fr-arr359fkygy5")); |
| 611 | 613 | expect(p.denyRead).not.toContain(p.tmpDir); |
| 612 | 614 | expect(p.denyRead).toContain(neighbour); |
| 615 | expect(p.denyRead).toContain(otherRun); | |
| 616 | // The data directory is in the temp directory too: it is not denied whole, | |
| 617 | // so the agent still reaches its own working directory. | |
| 618 | const under = (path: string) => p.denyRead.some((d) => path === d || path.startsWith(`${d}/`)); | |
| 619 | expect(under(join(data, "crew", "scottishbot", "01repo", "7"))).toBe(false); | |
| 613 | 620 | expect(p.denyRead).toContain(join(data, "forge.db")); |
| 614 | 621 | }); |
| 615 | 622 |
test/feed-composer-language.test.ts +3 −1
| @@ -81,7 +81,9 @@ test("a repository name with markup in it is escaped in the select, never raw", | ||
| 81 | 81 | app.db.query("UPDATE repo SET name = ?1 WHERE id = ?2").run('x"><img src=x onerror=alert(1)>', made.value.id); |
| 82 | 82 | const form = await composerOf(cookie); |
| 83 | 83 | expect(form).not.toContain("<img"); |
| 84 | expect(form).not.toContain('x">'); | |
| 84 | // Anchored on the slug: a bare 'x">' also matches data-draft="<user id>" whenever | |
| 85 | // the random id ends in "x" (#10486), which failed this test with nothing wrong. | |
| 86 | expect(form).not.toContain('alice/x"'); | |
| 85 | 87 | expect(form).toContain("alice/x"><img src=x onerror=alert(1)>"); |
| 86 | 88 | }); |
| 87 | 89 |