Skip to content

fix: verify automation re-export path stays inside the automations root - #33

Open
mittalpk wants to merge 1 commit into
microsoft:mainfrom
mittalpk:fix/automation-reexport-path-containment
Open

fix: verify automation re-export path stays inside the automations root#33
mittalpk wants to merge 1 commit into
microsoft:mainfrom
mittalpk:fix/automation-reexport-path-containment

Conversation

@mittalpk

Copy link
Copy Markdown

Fixes #17.

What's wrong

AutomationBuilder.exportAutomation() derives the re-export directory from a persisted automation's exportedPath:

let dir = prior?.exportedPath ? path.dirname(prior.exportedPath) : path.join(root, name);

with no check that it still resolves inside automationsRoot(), before mkdirSync + writeFileSync(automation.json) into it. prior comes from built-automation.json, persisted under the sessions root — a separate directory from the automations root. A relocated or tampered exportedPath in that file causes automation.json to be written outside the automations root on re-export.

The sibling Skill Builder already guards the equivalent re-install path with an isInside(root, dir) containment check (electron/skillbuilder/builder.ts) — this defense-in-depth guard was just missing for the Automation Builder's re-export path.

Fix

Exported isInside from skillbuilder/builder.ts and reused it in automationbuilder/builder.ts: only reuse the prior directory when it's still inside the automations root, otherwise fall back to a fresh, non-colliding directory — this is exactly the reuse-decision pattern exportSkill() already uses, applied here for consistency rather than inventing a new approach.

How I tested it

  • Added electron/automationbuilder/builder.test.ts (registered in package.json's test script), two cases:
    1. Seeds a persisted built-automation.json whose exportedPath points outside the automations root (an outsideRoot temp dir), calls AutomationBuilder.create() with a fully-formed plan (no live Copilot session needed — create() is deterministic when passed an explicit plan, per its own docstring), and asserts the resulting export path stays inside the automations root and nothing is written into the attacker-controlled directory.
    2. Confirms the existing, intended behavior is preserved: a prior export path that is inside the automations root is still reused as before.
  • Confirmed test 1 fails on unpatched builder.ts via git stash — the exact reported bug reproduces, automation.json lands in the outside directory. Passes after the fix.
  • npm run typecheck — clean.
  • npm test — 59/60 subtests pass; 1 pre-existing failure (writeDebugBundle zips the whole session... in electron/debug-bundle.test.ts) confirmed to fail identically on clean main with none of this PR's changes present — unrelated to this change.

This PR was developed with AI-assisted tooling (Claude Code); I reviewed the diagnosis, the fix, and the tests, and ran typecheck/tests locally as noted above.

exportAutomation() derived the re-export directory from a persisted
automation's exportedPath (path.dirname(prior.exportedPath)) with no
check that it still resolves inside automationsRoot(), then
mkdirSync + writeFileSync(automation.json) into it. A relocated or
tampered exportedPath in the persisted built-automation.json (which
lives under the sessions root, a separate directory) causes
automation.json to be written outside the automations root.

The sibling Skill Builder already guards the equivalent re-install
path via an isInside(root, dir) containment check
(electron/skillbuilder/builder.ts); this defense-in-depth guard was
missing for the Automation Builder's re-export path.

Fixed by exporting isInside from skillbuilder/builder.ts and reusing
it here: only reuse the prior directory when it's still inside the
automations root, otherwise fall back to a fresh, non-colliding
directory (matching the exact reuse-decision pattern already used by
exportSkill()).

Fixes microsoft#17.
@mittalpk

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Medium] Automation re-export doesn't verify the target stays inside the automations root

1 participant