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
9 changes: 4 additions & 5 deletions integration-tests/interactive/cron-interactive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,14 @@ function makeEnv(): NodeJS.ProcessEnv {
);

await session.idle(5000);
await session.send('Reply with exactly USERPRIORITY77 nothing else');
const userPriorityMarker = 'USERPRIORITY77';
await session.send(`Reply with exactly ${userPriorityMarker} nothing else`);

await session.waitForScreen(
(scr) => scr.includes('USERPRIORITY77'),
(scr) =>
scr.indexOf(userPriorityMarker) !== scr.lastIndexOf(userPriorityMarker),
'model response containing USERPRIORITY77',
);

const screen = await session.screen();
expect(screen).toContain('Type your message');
});

it(
Expand Down
20 changes: 14 additions & 6 deletions packages/core/src/utils/shellAstParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ describe('isShellCommandReadOnlyAST', () => {
// =========================================================================

describe('classifyShellCommandSafety', () => {
const maxClassificationCpuMs = 1000;

it.each([
'ls -la',
'git status --short',
Expand Down Expand Up @@ -908,20 +910,23 @@ describe('classifyShellCommandSafety', () => {
expect(await classifyShellCommandSafety(command)).toBe('unknown');
});

it('classifies deeply nested redirected substitutions without repeated traversal', async () => {
it('classifies deeply nested redirected substitutions within the CPU budget', async () => {
const commands = ['git status', 'git status'];
for (let depth = 0; depth < 20; depth++) {
commands[0] = `echo $(${commands[0]}) < /dev/null`;
commands[1] = `< <(${commands[1]}) cat`;
}
const startedAt = performance.now();
const startedCpuUsage = process.cpuUsage();
await expect(
Promise.all(commands.map(classifyShellCommandSafety)),
).resolves.toEqual(['unknown', 'unknown']);
expect(performance.now() - startedAt).toBeLessThan(1000);
const cpuUsage = process.cpuUsage(startedCpuUsage);
expect((cpuUsage.user + cpuUsage.system) / 1000).toBeLessThan(
maxClassificationCpuMs,
);
});

it('classifies adversarial rule inputs in bounded time', async () => {
it('classifies adversarial rule inputs within the CPU budget', async () => {
const backslashes = '\\'.repeat(10_000);
const repeatedSed = 'p;'.repeat(10_000);
const repeatedPrint = 'print value; '.repeat(10_000);
Expand All @@ -935,7 +940,7 @@ describe('classifyShellCommandSafety', () => {
`find . ${repeatedFindExec}`,
`git status ${unmatchedBraces}`,
];
const startedAt = performance.now();
const startedCpuUsage = process.cpuUsage();
await expect(
Promise.all(commands.map(classifyShellCommandSafety)),
).resolves.toEqual([
Expand All @@ -946,7 +951,10 @@ describe('classifyShellCommandSafety', () => {
'unknown',
'read-only',
]);
expect(performance.now() - startedAt).toBeLessThan(1000);
const cpuUsage = process.cpuUsage(startedCpuUsage);
expect((cpuUsage.user + cpuUsage.system) / 1000).toBeLessThan(
maxClassificationCpuMs,
);
});
});

Expand Down
Loading