Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/opencode-inference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ jobs:
run: python3 -B scripts/opencode_ci.py export
- name: Retain original closed evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: opencode-qwen-${{ github.run_id }}-${{ github.sha }}
path: build/ci-public-receipts/*.json
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/opencode-public-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ jobs:
- name: Collect only the core's closed allowlisted receipts
if: always()
run: python3 -B scripts/public_code_ci.py export
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: always()
with:
name: public-code-proposal-${{ github.run_id }}-${{ github.sha }}
Expand Down
21 changes: 17 additions & 4 deletions scripts/smoke_editor_ui.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Disposable guest only. Real CDP keyboard/mouse input, never a VS Code API shim.
'use strict';
const fs = require('node:fs/promises');
const {constants} = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const {spawnSync} = require('node:child_process');
Expand Down Expand Up @@ -58,11 +59,23 @@ async function privateDirectory(value) {
(info.mode & 0o7777) === 0o700, 'project_scope');
}

const checkFixture = value => demand(value, 'fixture_failed');
async function fixtureFile(project, name, max = 65536) {
const file = path.join(project, name), info = await fs.lstat(file);
demand(info.isFile() && !info.isSymbolicLink() && info.nlink === 1 && info.uid === process.getuid() &&
info.size <= max && !(info.mode & 0o022), 'fixture_failed');
return fs.readFile(file);
const file = path.join(project, name);
const handle = await fs.open(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
try {
const info = await handle.stat();
checkFixture(info.isFile() && info.nlink === 1 && info.uid === process.getuid() && info.size <= max && !(info.mode & 0o022));
const bytes = Buffer.alloc(info.size + 1); let size = 0;
while (size < bytes.length) {
const { bytesRead } = await handle.read(bytes, size, bytes.length - size, size);
if (bytesRead === 0) break;
size += bytesRead;
}
const after = await handle.stat();
checkFixture(size === info.size && ['size', 'mtimeMs', 'mode', 'uid', 'gid'].every(key => info[key] === after[key]));
return bytes.subarray(0, size);
} finally { await handle.close(); }
}

function journal(bytes) {
Expand Down
20 changes: 17 additions & 3 deletions scripts/smoke_opencode_inference.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs/promises');
const {constants} = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const {createHash} = require('node:crypto');
Expand Down Expand Up @@ -81,9 +82,22 @@ async function owned(file, directory = false) {
return info;
}
async function contents(project, name, maximum = 8192) {
const target = path.join(project, name), info = await owned(target);
assert.ok(info.size > 0 && info.size <= maximum);
return fs.readFile(target);
const target = path.join(project, name);
assert.equal(await fs.realpath(target), target);
const handle = await fs.open(target, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
try {
const info = await handle.stat();
assert.ok(info.isFile() && info.uid === process.getuid() && (info.mode & 0o6022) === 0 && info.size > 0 && info.size <= maximum);
const bytes = Buffer.alloc(info.size + 1); let size = 0;
while (size < bytes.length) {
const { bytesRead } = await handle.read(bytes, size, bytes.length - size, size);
if (bytesRead === 0) break;
size += bytesRead;
}
const after = await handle.stat();
assert.ok(size === info.size && ['size', 'mtimeMs', 'mode', 'uid', 'gid'].every(key => info[key] === after[key]));
return bytes.subarray(0, size);
} finally { await handle.close(); }
}

function createTrialVerifier(project, createVerifier = createWorkspaceVerifier) {
Expand Down
110 changes: 110 additions & 0 deletions tests/descriptor-reads.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-3.0-only
// Isolated actual reader bodies with real files/descriptors, no guest/application execution.
'use strict';
const {test} = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const fsp = require('node:fs/promises');
const path = require('node:path');
const os = require('node:os');
const vm = require('node:vm');
const ROOT = path.resolve(__dirname, '..');
const readers = [
{
"source": "scripts/smoke_editor_ui.cjs",
"name": "fixtureFile",
"sync": false,
"args": "project",
"rootOnly": false
},
{
"source": "scripts/smoke_opencode_inference.cjs",
"name": "contents",
"sync": false,
"args": "project",
"rootOnly": false
},
{
"source": "tests/public-code-file.test.cjs",
"name": "readFixtureBytes",
"sync": true,
"args": "file",
"rootOnly": false
}
];
function reader(config, afterStat = () => {}, beforeRead = () => {}) {
const source = fs.readFileSync(path.join(ROOT, config.source), 'utf8');
const start = source.indexOf((config.sync ? 'function ' : 'async function ') + config.name + '(');
assert.ok(start >= 0);
const end = source.indexOf('\n}', start) + 2;
assert.ok(end > start);
const handles = [], closed = [], capacities = [];
const wrappedFS = {...fs, ...(!config.sync ? fsp : {})};
if (config.sync) {
wrappedFS.openSync = (...args) => { const fd = fs.openSync(...args); handles.push(fd); return fd; };
wrappedFS.fstatSync = fd => { const info = fs.fstatSync(fd); afterStat(); return info; };
wrappedFS.readSync = (fd, bytes, ...args) => {
capacities.push(bytes.length); beforeRead(); return fs.readSync(fd, bytes, ...args);
};
wrappedFS.closeSync = fd => { closed.push(fd); return fs.closeSync(fd); };
}
async function open(...args) {
const handle = await fsp.open(...args); handles.push(handle.fd);
return {stat: async () => {const info = await handle.stat(); afterStat(); return info;},
read: async (bytes, ...rest) => {capacities.push(bytes.length); beforeRead(); return handle.read(bytes, ...rest);},
close: async () => {closed.push(handle.fd); await handle.close();}};
}
wrappedFS.open = open;
const demand = value => assert.ok(value);
const fn = vm.runInNewContext('(' + source.slice(start, end) + ')', {fs: wrappedFS, open,
constants: fs.constants, realpath: fsp.realpath, realpathSync: fs.realpathSync,
path, Buffer, process, assert, insist: demand, require: demand, demand, checkFixture: demand,
MAX_BYTES: 2 * 1024 * 1024});
const call = file => config.args === 'expected' ? fn(file, {bytes: 12})
: config.args === 'project' ? fn(path.dirname(file), path.basename(file)) : fn(file);
return {call, handles, closed, capacities};
}
function fixture(t) {
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'vp-descriptor-'));
fs.chmodSync(directory, 0o700);
const file = path.join(directory, 'fixture');
fs.writeFileSync(file, 'trusted-data', {mode: 0o600});
t.after(() => fs.rmSync(directory, {recursive: true, force: false}));
return {directory, file};
}
for (const config of readers) {
test(config.name + ': leaf symlink is rejected without reading its target', async t => {
const f = fixture(t), linked = path.join(f.directory, 'link');
fs.symlinkSync(f.file, linked);
const r = reader(config);
await assert.rejects(async () => r.call(linked));
assert.equal(r.capacities.length, 0); assert.deepEqual(r.closed, r.handles);
});
if (config.rootOnly && process.getuid() !== 0) {
test(config.name + ': unprivileged ownership fails closed and closes descriptor', async t => {
const f = fixture(t), r = reader(config);
await assert.rejects(async () => r.call(f.file));
assert.equal(r.capacities.length, 0); assert.deepEqual(r.closed, r.handles);
});
continue; // No simulated root authority in an unprivileged local test.
}
test(config.name + ': pathname replacement after stat reads the checked descriptor', async t => {
const f = fixture(t); let replaced = false;
const r = reader(config, () => {
if (replaced) return; replaced = true;
fs.renameSync(f.file, path.join(f.directory, 'original'));
fs.writeFileSync(f.file, 'untrusted!!!', {mode: 0o600});
});
assert.equal((await r.call(f.file)).toString(), 'trusted-data');
assert.equal(replaced, true); assert.deepEqual(r.closed, r.handles);
});
test(config.name + ': concurrent growth is bounded and closes on rejection', async t => {
const f = fixture(t); let changed = false;
const r = reader(config, () => {}, () => {
if (changed) return; changed = true; fs.appendFileSync(f.file, Buffer.alloc(1024 * 1024));
});
await assert.rejects(async () => r.call(f.file));
assert.ok(r.capacities.length > 0); assert.ok(r.capacities.every(size => size === 13));
assert.deepEqual(r.closed, r.handles);
});
}
18 changes: 17 additions & 1 deletion tests/public-code-file.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ const path = require('node:path');
const {createHash} = require('node:crypto');
const {capturePublicCodeFile, applyPublicCodeFile} = require('../src/public-code-file.cjs');
const {fixture: publicCore, INPUT, updateReport} = require('./public-code-fixture.cjs');
function readFixtureBytes(file) {
const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
try {
const info = fs.fstatSync(fd);
assert.ok(info.isFile() && info.uid === process.getuid() && info.size <= 65536);
const bytes = Buffer.alloc(info.size + 1); let size = 0;
while (size < bytes.length) {
const count = fs.readSync(fd, bytes, size, bytes.length - size, size);
if (count === 0) break;
size += count;
}
const after = fs.fstatSync(fd);
assert.ok(size === info.size && ['size', 'mtimeMs', 'mode', 'uid', 'gid'].every(key => info[key] === after[key]));
return bytes.subarray(0, size);
} finally { fs.closeSync(fd); }
}
function fixture(t) {
const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'vp-public-file-'));
const file = path.join(workspace, 'selected.js');
Expand Down Expand Up @@ -66,7 +82,7 @@ test('approved exact raw replacement changes only selected file after validated
const applied = await f.apply({approve: value => {edit = value; return true;}});
assert.equal(applied.applied, true);
assert.equal(fs.readFileSync(f.file, 'utf8'), 'export const value = 2;\n');
assert.equal(fs.readFileSync(tests, 'utf8'), 'original independent test bytes');
assert.equal(readFixtureBytes(tests).toString('utf8'), 'original independent test bytes');
assert.notEqual(fs.statSync(f.file).ino, oldIdentity); // No write through old inode.
assert.equal(fs.statSync(f.file).mode & 0o777, 0o600);
assert.equal(edit.replacement, 'export const value = 2;\n');
Expand Down
5 changes: 3 additions & 2 deletions tests/test_opencode_native_cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,12 @@ def test_guest_keeps_original_task_and_inference_budgets_and_joins_native_unit(s
"if report['units_empty'] and report['provision_group_joined']:"):
self.assertIn(text, source)
task_driver = (ROOT / 'scripts/smoke_opencode_inference.cjs').read_bytes()
# Exact source with opt-in output selection, usable in a shallow checkout.
# Exact source with opt-in output selection and descriptor-bound fixture reads.
# The reviewed delta changes only contents() and its fs.constants import.
# Focused JS tests retain the original prompt, approvals and round bounds;
# no regenerated answers or claim of model quality.
self.assertEqual(hashlib.sha256(task_driver).hexdigest(),
'92ca6da4a1cff07f07fd9e56afe28f853c2868f7d111c6cdc5350145bb54cba0')
'0f395f215d11b3ebc868299a7695908511724778b72d2966b9669ccaf20ccf13')

def test_native_memory_requires_actual_zero_swap_and_oom_not_just_limits(self):
with tempfile.TemporaryDirectory() as temp, patch.object(TRIAL, 'cgroup', return_value=Path(temp)):
Expand Down
Loading