mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
0d4e58914e
Work-in-progress snapshot preserved for the dedicated #2026 PR. NOT for merge as-is: the migration is incomplete (injected-executor ...WithAdb helpers, packages/platform-* host ports, and ~30 test files still assert the legacy ['shell', ...] shape), and a partially enforced seam is exactly what #2026 must avoid landing. Mechanism (complete, green in isolation): - packages/kernel/src/shell-safe.ts: branded ShellSafe plus sh.lit/lits/arg/num/raw, byte-identical to raw argv for safe-charset input (parity test). - runAndroidShell/runAndroidExecOut, runHarmonyShell, runLimrunAndroidShell funnels; a raw shell/exec-out argv is rejected at the adb executor, hdc, and limrun boundaries (catches variable-built argv, which a static gate cannot). - src/platforms/android/__tests__/device-shell-boundary.test.ts: planted-red tests for raw and indirect/variable-built argv, plus a ts-expect-error proving raw strings do not typecheck. - scripts/shell-safe: AST backstop holding sh.raw and ShellSafe casts to sites carrying a shell-safe-approved comment with a reason. Migrated so far: android input-actions/perf*/settings/screenshot/notifications/ permission-grant-state/app-deployment-resolution, all harmonyos, limrun, daemon/android-system-dialog, platform-runtime-runtime-hints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wb8EuoySL26UsdtCtRj5X8
43 lines
1.5 KiB
TypeScript
43 lines
1.5 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { findShellSafeMatches, unapprovedShellSafeMatches } from './model.ts';
|
|
|
|
function scan(source: string, path = 'src/x.ts') {
|
|
return findShellSafeMatches([{ path, source }]);
|
|
}
|
|
|
|
test('an approved sh.raw is accepted', () => {
|
|
const src = [
|
|
'const x = 1;',
|
|
'// shell-safe-approved: fixed rm on a shellQuote-escaped device path',
|
|
'const cmd = sh.raw(`rm -f ${shellQuote(p)}`);',
|
|
].join('\n');
|
|
const matches = scan(src);
|
|
assert.equal(matches.length, 1);
|
|
assert.equal(matches[0]?.approved, true);
|
|
assert.deepEqual(unapprovedShellSafeMatches(matches), []);
|
|
});
|
|
|
|
test('an unapproved sh.raw is flagged', () => {
|
|
const matches = scan('const cmd = sh.raw(userText);');
|
|
assert.equal(matches.length, 1);
|
|
assert.equal(unapprovedShellSafeMatches(matches).length, 1);
|
|
});
|
|
|
|
test('a bare marker with no reason does not approve', () => {
|
|
const src = ['// shell-safe-approved:', 'const cmd = sh.raw(x);'].join('\n');
|
|
assert.equal(unapprovedShellSafeMatches(scan(src)).length, 1);
|
|
});
|
|
|
|
test('an as-ShellSafe cast outside the constructor module is flagged', () => {
|
|
const matches = scan('const s = value as ShellSafe;');
|
|
assert.equal(matches.length, 1);
|
|
assert.equal(matches[0]?.kind, 'as-shell-safe');
|
|
assert.equal(unapprovedShellSafeMatches(matches).length, 1);
|
|
});
|
|
|
|
test('as-ShellSafe casts inside the kernel constructor module are exempt', () => {
|
|
const matches = scan('return value as ShellSafe;', 'packages/kernel/src/shell-safe.ts');
|
|
assert.deepEqual(matches, []);
|
|
});
|