| 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | |
| 3 | import { embeddedScripts } from "./embeddedScripts"; |
| 4 | |
| 5 | /** Each script's language, its text, and the part of the command that spells it. */ |
| 6 | function scriptsOf(command: string) { |
| 7 | return embeddedScripts(command).map((script) => { |
| 8 | // Every character maps inside the command, in order. |
| 9 | script.starts.forEach((start, index) => { |
| 10 | expect(start).toBeLessThan(script.ends[index]!); |
| 11 | if (index > 0) expect(start).toBeGreaterThanOrEqual(script.ends[index - 1]!); |
| 12 | }); |
| 13 | return [ |
| 14 | script.language, |
| 15 | script.text, |
| 16 | command.slice(script.starts[0], script.ends.at(-1)), |
| 17 | ] as const; |
| 18 | }); |
| 19 | } |
| 20 | |
| 21 | const pythonScript = `from pathlib import Path |
| 22 | import json |
| 23 | |
| 24 | # print every package.json name and version |
| 25 | for p in sorted(Path('.').glob('**/package.json')): |
| 26 | data = json.loads(p.read_text()) |
| 27 | print(f"{data['name']:16} {data.get('version', '?')}") |
| 28 | `; |
| 29 | |
| 30 | describe("embeddedScripts", () => { |
| 31 | it("finds the script inside a Codex login-shell wrapper and the script inside that", () => { |
| 32 | // Recorded in the turn_interrupt_mid_tool Codex transcript |
| 33 | // (apps/server/src/orchestration-v2/testkit/fixtures). |
| 34 | const inner = `console.log('interrupt fixture tool started'); setTimeout(() => {}, 30000)`; |
| 35 | const command = `/bin/bash -lc "node -e \\"${inner}\\""`; |
| 36 | expect(scriptsOf(command)).toEqual([ |
| 37 | ["shellscript", `node -e "${inner}"`, `node -e \\"${inner}\\"`], |
| 38 | ["javascript", inner, inner], |
| 39 | ]); |
| 40 | }); |
| 41 | |
| 42 | it("highlights a heredoc fed to an interpreter in that interpreter's language", () => { |
| 43 | const command = `python3 - <<'PY'\n${pythonScript}PY`; |
| 44 | expect(scriptsOf(command)).toEqual([["python", pythonScript, pythonScript]]); |
| 45 | }); |
| 46 | |
| 47 | it("maps escaped characters back to both source characters", () => { |
| 48 | const command = `/bin/zsh -lc "python3 - <<'PY'\nprint(\\"\\$HOME\\")\nPY"`; |
| 49 | const [shell, python] = scriptsOf(command); |
| 50 | expect(shell?.[1]).toBe(`python3 - <<'PY'\nprint("$HOME")\nPY`); |
| 51 | expect(python).toEqual(["python", `print("$HOME")\n`, `print(\\"\\$HOME\\")\n`]); |
| 52 | }); |
| 53 | |
| 54 | it.each([ |
| 55 | [`python3 -c 'print(1)'`, "python", "print(1)"], |
| 56 | [`python3 -X dev -c 'print(1)'`, "python", "print(1)"], |
| 57 | [`py -3 -c 'print(1)'`, "python", "print(1)"], |
| 58 | [`C:\\Python312\\python.exe -c 'print(1)'`, "python", "print(1)"], |
| 59 | [`"C:\\Program Files\\Python312\\python.exe" -c 'print(1)'`, "python", "print(1)"], |
| 60 | [`node --eval "process.exit(0)"`, "javascript", "process.exit(0)"], |
| 61 | [`bun -e 'console.log(Bun.version)'`, "typescript", "console.log(Bun.version)"], |
| 62 | [`deno eval 'console.log(1)'`, "typescript", "console.log(1)"], |
| 63 | [`deno eval --config deno.json 'console.log(1)'`, "typescript", "console.log(1)"], |
| 64 | [`deno eval --env-file 'console.log(1)'`, "typescript", "console.log(1)"], |
| 65 | [`psql -f schema.sql -c 'select 1'`, "sql", "select 1"], |
| 66 | [`node -C development -e 'console.log(1)'`, "javascript", "console.log(1)"], |
| 67 | [`ruby -e 'puts 1'`, "ruby", "puts 1"], |
| 68 | [`ruby -I lib -e 'puts 1'`, "ruby", "puts 1"], |
| 69 | [`perl -E 'say 1'`, "perl", "say 1"], |
| 70 | [`sqlite3 app.db "select * from users"`, "sql", "select * from users"], |
| 71 | [`psql -d app -c 'select 1'`, "sql", "select 1"], |
| 72 | [`pwsh -NoProfile -Command "Get-ChildItem"`, "powershell", "Get-ChildItem"], |
| 73 | [`env FOO=1 sudo -u bob python3 -c 'print(1)'`, "python", "print(1)"], |
| 74 | [`env -- FOO=1 python3 -c 'print(1)'`, "python", "print(1)"], |
| 75 | [`node --eval="process.exit(0)"`, "javascript", "process.exit(0)"], |
| 76 | [`psql --command='select 1'`, "sql", "select 1"], |
| 77 | [`mysql -c -e 'select 1'`, "sql", "select 1"], |
| 78 | [`mysql -f app <<'EOF'\nselect 1;\nEOF`, "sql", "select 1;\n"], |
| 79 | [`psql app <<'EOF'\nselect 1;\nEOF`, "sql", "select 1;\n"], |
| 80 | [`echo "$(node -e 'console.log(1)')"`, "javascript", "console.log(1)"], |
| 81 | [`python3 <<< 'print(1)'`, "python", "print(1)"], |
| 82 | ])("finds the inline script in %s", (command, language, script) => { |
| 83 | expect(scriptsOf(command)).toEqual([[language, script, expect.any(String)]]); |
| 84 | }); |
| 85 | |
| 86 | it.each([[`psql -c 'select 1' -c 'select 2'`], [`sqlite3 app.db "select 1" "select 2"`]])( |
| 87 | "finds every statement a SQL shell runs: %s", |
| 88 | (command) => { |
| 89 | expect(scriptsOf(command).map(([language, text]) => [language, text])).toEqual([ |
| 90 | ["sql", "select 1"], |
| 91 | ["sql", "select 2"], |
| 92 | ]); |
| 93 | }, |
| 94 | ); |
| 95 | |
| 96 | it.each([ |
| 97 | ["the file `cat` writes", `cat > src/app.ts <<'EOF'\nconst a = 1;\nEOF`, "typescript"], |
| 98 | ["the file `tee` writes", `tee -a config.json <<EOF\n{}\nEOF`, "json"], |
| 99 | ["its delimiter", `kubectl apply -f - <<'YAML'\nkind: Pod\nYAML`, "yaml"], |
| 100 | ["the shell reading it", `bash <<'EOF'\necho hi\nEOF`, "shellscript"], |
| 101 | ])("takes a heredoc's language from %s", (_source, command, language) => { |
| 102 | expect(scriptsOf(command).map(([lang]) => lang)).toEqual([language]); |
| 103 | }); |
| 104 | |
| 105 | it.each([ |
| 106 | ["a script file", `python3 build.py <<EOF\ndata\nEOF`], |
| 107 | ["a module", `python3 -m json.tool <<EOF\n{}\nEOF`], |
| 108 | ["a SQL file", `psql -f migrate.sql <<EOF\ny\nEOF`], |
| 109 | ["a SQL file given with =", `psql --file=migrate.sql <<EOF\ny\nEOF`], |
| 110 | ["plain commands", `rg -n "foo" src && git status`], |
| 111 | ["an unnamed heredoc into a plain command", `git commit -F - <<'EOF'\nfix: x\nEOF`], |
| 112 | ["an empty script", `bash -lc ' '`], |
| 113 | ["a heredoc `cat` sends to stdout while stderr goes to a file", `cat 2>log.ts <<'EOF'\nx\nEOF`], |
| 114 | ])("finds nothing for %s", (_case, command) => { |
| 115 | expect(scriptsOf(command)).toEqual([]); |
| 116 | }); |
| 117 | |
| 118 | it("trusts nothing in a command whose quoting does not parse", () => { |
| 119 | // Codex's secret redaction dropped the backslash of an escaped quote, so |
| 120 | // the -lc argument ends early and the rest no longer balances. |
| 121 | const command = `/bin/zsh -lc "python3 -c 'token=[REDACTED_SECRET]"apiToken\\"]; print(token)'"`; |
| 122 | expect(scriptsOf(command)).toEqual([]); |
| 123 | }); |
| 124 | }); |