Repository navigation
fix(security): convert recording-tools.ts execSync → execFileSync (PR 3/5, #1451) - #1466
Conversation
… 3/5, sonichi#1451) Largest single file in the audit: 21 execSync calls eliminated. Key fixes: - scrollDown osascript ${tmpScroll}: temp file path passed as argv, no shell interpolation (bassilkhilo-ag2 Low-Med item) - open "${recPath}": file path from findRecording() passed as argv - All AppleScript -e clauses split into individual argv entries - python3 record.py ${action}: enum-constrained arg as separate argv - ffmpeg -filters: bin from hardcoded candidates/env, argv array - ls glob: static literal in /bin/sh -c wrapper (same as PR sonichi#1462) - Import: removed execSync, kept execFileSync only Zero execSync remaining in recording-tools.ts. Refs: sonichi#1451, bassilkhilo-ag2 triage
|
LGTM — CI green (2/2). All conversions are correct:
Ready to merge. |
liususan091219
left a comment
There was a problem hiding this comment.
Review — recording-tools.ts execSync → execFileSync (PR 3/5, #1451)
Verdict. Correct and complete. This is the largest file in the sweep (21 conversions) and every one checks out: argv arrays where possible, /bin/sh -c with a static literal only where the glob+pipe genuinely needs a shell, and output handling preserved at each site. The import drops execSync entirely and no execSync calls remain; the pre-existing execFileSync calls are correctly left untouched.
Things I like
recording-tools.ts:27(ffmpeg-filters) done carefully. The oldexecSync(\${bin} -filters 2>&1`)both interpolatedbin*and* used shell2>&1to fold stderr into stdout (ffmpeg writes-filtersto stderr). The new code drops the shell but **preserves the capture** viastdio: ['pipe','pipe','pipe']+encoding:'utf-8', then.includes('subtitles')`. Subtle and easy to get wrong — losing the stderr capture here would silently break subtitle-ffmpeg detection. Handled correctly.- AppleScript
-esplitting. Every multi-clauseosascript -e '...' -e '...'is split into one argv entry per-eclause, preserving clause ordering exactly. The ~14 osascript conversions are mechanically faithful — the QuickTime play/pause/close and Chrome-scroll sites match the originals 1:1. open "${recPath}"→execFileSync('open', [recPath]).recPathcomes fromfindRecording()(filesystem-derived), a low-but-real interpolation site, now a clean argv boundary.- Glob correctly kept under
/bin/sh -cwith a static literal.ls -t /tmp/sutando-recording-*.mov | grep -v narrated | grep -v subtitled | head -1needs the shell for glob + two pipes; the command string is constant → no injection surface. Same pattern as #1462. Output handling (.toString().trim()) preserved. - python3
record.pycalls — argv arrays, with thez.enumactionpassed as a discrete arg. Even enum-constrained, treating it as argv rather than string-concat is the defense-in-depth right answer.JSON.parse(raw)downstream unaffected since.toString().trim()is preserved. swift src/scroll-wheel.swift 1→ argv split with static args.
Non-blocking observations
encoding/.toString()parity is the thing to watch in a file this size, and it holds. Every site that previously didexecSync(...).toString()still does.toString()on the new Buffer-returning call; the one site that switched toencoding:'utf-8'(ffmpeg-filters) then uses.includes()directly, which is correct because encoding makes it return a string. No mixed Buffer/string bug slipped in.- Path-pinning is mixed —
osascriptpinned to/usr/bin/osascript, butpython3/swift/openarePATH-resolved. Matches the file's prior behavior, so no regression — noting only if a future "pin all binaries" pass happens. - Static-literal
/bin/sh -cinvariant is the same load-bearing constraint as #1462 — safe today, needs care if anyone later interpolates into it. - Best-effort
try/catchsemantics preserved at every site; all stay synchronousexecFileSync, so timing/ordering of the inline tools is unchanged.
Cross-PR linkage
PR 3 of the class with #1452 and #1462, and the largest. It reuses #1462's /bin/sh -c + static-literal glob pattern verbatim and the osascript--e-splitting pattern matches #1452's conversions. Consistent with prior repo execFile work (#530, #527). The PR's claim that this "closes the substantive security work for #1451" (remaining files hardcoded/low-risk) matches what I see — the high-value interpolation sites are the open/prepend-image.sh/meetUrl ones already addressed across the three PRs.
Tests
No test ships with this PR. Given 21 sites, most are static and mechanically verifiable from the diff; the two worth a regression test are the ffmpeg -filters stderr-capture (assert subtitles detection still works) and the findRecording glob. Non-blocking — macOS/ffmpeg-runtime-dependent surface, hard to unit-test cheaply, conversions are 1:1.
Ready to merge
Yes. The largest file in the sweep, converted carefully — the one genuinely tricky site (ffmpeg stderr capture) is handled correctly, output parity holds throughout, and shell-required sites are safe-by-construction.
— Lucy (Mac Studio bot)
bassilkhilo-ag2
left a comment
There was a problem hiding this comment.
21 execSync calls converted across 5 sites:
ffmpeg -filters: static binary path from allowlist["ffmpeg", "/opt/homebrew/bin/ffmpeg", ...]→execFileSync(bin, ["-filters"], {stdio: ["pipe","pipe","pipe"]}). Note: original used2>&1to capture stderr into stdout; new captures stdout only. ffmpeg sends its-filterslist to stdout so theincludes("subtitles")check still works correctly.python3 skills/screen-record/scripts/record.py stop→execFileSync("python3", ["skills/screen-record/scripts/record.py", "stop"])— correct.ls -t /tmp/sutando-recording-*.mov ...→execFileSync("/bin/sh", ["-c", "..."])with static command — correct, glob expansion needs shell.osascript ${tmpScroll}→execFileSync("/usr/bin/osascript", [tmpScroll])— correct, tmpScroll is a path we just wrote, hardcoded to absolute binary.
LGTM.
sonichi
left a comment
There was a problem hiding this comment.
LGTM. execSync removed entirely; all calls through execFileSync. Shell-dependent commands via /bin/sh -c, static binaries via argv array. osascript via /usr/bin/osascript with tmpfile as arg. Stand: Echo Act IV Pro
sonichi
left a comment
There was a problem hiding this comment.
LGTM. execSync removed entirely; all calls through execFileSync. Shell-dependent commands via /bin/sh -c, static binaries via argv array. osascript via /usr/bin/osascript with tmpfile as arg. Stand: Echo Act IV Pro
…rator checklist + workspace-sync node_modules Per Chi's PR #1472 comments (#design 2026-06-06): **CHANGELOG.md v0.3.0 additions:** - Fixed → Migration subsection: Lucy's Maddy v0.8 5-bug sweep (#1475 #1478 #1482 #1483 #1484) - Fixed → Cross-platform: #1476 (uname -s branch for preflight byte count) - Fixed → Security: #1452 #1462 #1466 (execSync → execFileSync sweep) - Added: #1467 (bridges inject skill instructions into owner task files) - Workspace contract rollup → M1: also reference #1473 (pre-flight scan + per-file progress reporter) **KNOWN_ISSUES.md additions** (from migration-thread digest 1512359988834730014): - Pre-migration operator checklist (7 items) — vault URL, remove shell-rc env var, grep .git/info/exclude, confirm wsId, stagger hosts, restart services, post-migration sanity check - Identity-collision rule — exactly one host at a time can hold a Discord token (also applies to Twilio + other per-identity creds) **docs/workspace-sync.md** pre-flight checklist: - Added `node_modules/` row per Chi's PR #1472 comment (covers vault-sync side; #1482 already handles the backup-side via `_BACKUP_DEFAULT_EXCLUDES`) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@cla-assistant check |
Summary — PR 3/5 for #1451
Converts all 21
execSynccalls inrecording-tools.tstoexecFileSync. This is the largest single file in the audit.Key vulnerabilities fixed
osascript ${tmpScroll}(line 346)execFileSync("/usr/bin/osascript", [tmpScroll])— argv arrayopen "${recPath}"(line 519)execFileSync("open", [recPath])— argv arraypython3 ... ${action}(line 679)execFileSync("python3", [script, action])— argv array${bin} -filters 2>&1(line 27)execFileSync(bin, ["-filters"])— argv arrayAll other conversions
Details
After this PR
Remaining files per @bassilkhilo-ag2 triage are all Low/no risk:
This PR closes the substantive security work for #1451.
Part of #1451 systematic hardening.