shell-quote.spec.ts 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. /**
  2. * Unit tests for the shell-quoting safety boundary, plus a REAL round-trip:
  3. * every adversarial value, quoted, must survive `bash -c "printf '%s' <quoted>"`
  4. * byte-for-byte — proving the quoting is inert in an actual shell, not just
  5. * against a mental model of one.
  6. */
  7. import { describe, expect, it } from 'vitest'
  8. import { spawnSync } from 'node:child_process'
  9. import { singleQuote } from '@deepseek-ai/dsh-tool-fs-search'
  10. /** Adversarial values a model could pass as pattern / path / include. */
  11. const HOSTILE: readonly string[] = [
  12. 'plain',
  13. 'with spaces',
  14. "it's got 'quotes'",
  15. '"double quoted"',
  16. '$(rm -rf /tmp/nope)',
  17. '`touch /tmp/nope`',
  18. '$HOME and ${PATH}',
  19. 'semi;colon && chain || pipe | bg &',
  20. 'newline\nin the middle',
  21. '-leading-dash',
  22. '--leading-double-dash',
  23. '*?[a-z]{x,y}',
  24. '!bang',
  25. '\\backslash\\',
  26. '~tilde',
  27. '# not a comment',
  28. '>redirect <input 2>&1',
  29. ]
  30. describe('singleQuote', () => {
  31. it('wraps a plain value in single quotes', () => {
  32. expect(singleQuote('abc')).toBe("'abc'")
  33. })
  34. it("rewrites embedded single quotes as '\\''", () => {
  35. expect(singleQuote("a'b")).toBe("'a'\\''b'")
  36. expect(singleQuote("''")).toBe("''\\'''\\'''")
  37. })
  38. it.each(HOSTILE.map(value => [JSON.stringify(value), value] as const))(
  39. 'round-trips %s through a real bash -c unchanged',
  40. (_label, value) => {
  41. const result = spawnSync('bash', ['-c', `printf '%s' ${singleQuote(value)}`], { encoding: 'utf8' })
  42. expect(result.status).toBe(0)
  43. expect(result.stdout).toBe(value)
  44. },
  45. )
  46. it('a quoted command substitution does not execute (the world stays untouched)', () => {
  47. const canary = `/tmp/dsh-quote-canary-${process.pid}`
  48. const result = spawnSync('bash', ['-c', `printf '%s' ${singleQuote(`$(touch ${canary})`)}`], { encoding: 'utf8' })
  49. expect(result.stdout).toBe(`$(touch ${canary})`)
  50. // The canary file must NOT exist — the substitution stayed literal.
  51. expect(spawnSync('test', ['-e', canary]).status).not.toBe(0)
  52. })
  53. })