Summary
Parser.resolveTemplatePath() passes a workspace-controlled path straight to fs.readFileSync without any containment check, so a crafted .arb file (or l10n.yaml) can make the extension read any file the editing user can open, outside the project.
- Version: 0.2.2 (HEAD
a6fa5a3)
- Source: the
@@x-template field of an .arb file, or arb-dir / template-arb-file in l10n.yaml
- Sink:
src/messageParser.ts:243 — fs.readFileSync(templatePath, "utf8")
- Resolve:
src/messageParser.ts:207-218 — no .. rejection, no workspace containment, absolute paths returned as-is
Steps to reproduce
- In any Flutter/ARB project, set the template field of an
.arb file:
{ "@@locale": "es", "@@x-template": "../../../../../../../../../../etc/passwd" }
(path.join clamps surplus .. at the filesystem root, so this lands on /etc/passwd regardless of where the repo is cloned; an absolute path works too.)
- Open the project in VS Code with the ARB Editor extension installed and open the
.arb file.
- The extension's parser runs and calls
fs.readFileSync("/etc/passwd") — a file entirely outside the workspace.
Impact
- Arbitrary local file read. Any file readable by the user can be opened by the extension as a side effect of editing a repository.
- Disclosure channel. For JSON-with-string-values targets, the top-level keys are echoed into the Problems panel as
Missing messages from template: <keys> (src/diagnose.ts), surfacing file contents (key names) to the editing user — useful for exfiltration via a repo the attacker later inspects, or simply for tricking a victim.
- Extension-host DoS (sub-case). The read is synchronous and follows symlinks; pointing the template at
/dev/zero, a FIFO, or a multi-GB file blocks the extension host.
The same unchecked path also reaches the sink through the l10n.yaml (arb-dir + template-arb-file) branch.
Suggested fix
Resolve the candidate path and reject anything that escapes the workspace folder (fall back to the document directory when there is no workspace folder). Legitimate in-project templates are unaffected. PR attached.
This was reported through Google's VRP and closed as out of scope for that program; filing here so the project can fix it directly.
Summary
Parser.resolveTemplatePath()passes a workspace-controlled path straight tofs.readFileSyncwithout any containment check, so a crafted.arbfile (orl10n.yaml) can make the extension read any file the editing user can open, outside the project.a6fa5a3)@@x-templatefield of an.arbfile, orarb-dir/template-arb-fileinl10n.yamlsrc/messageParser.ts:243—fs.readFileSync(templatePath, "utf8")src/messageParser.ts:207-218— no..rejection, no workspace containment, absolute paths returned as-isSteps to reproduce
.arbfile:{ "@@locale": "es", "@@x-template": "../../../../../../../../../../etc/passwd" }path.joinclamps surplus..at the filesystem root, so this lands on/etc/passwdregardless of where the repo is cloned; an absolute path works too.).arbfile.fs.readFileSync("/etc/passwd")— a file entirely outside the workspace.Impact
Missing messages from template: <keys>(src/diagnose.ts), surfacing file contents (key names) to the editing user — useful for exfiltration via a repo the attacker later inspects, or simply for tricking a victim./dev/zero, a FIFO, or a multi-GB file blocks the extension host.The same unchecked path also reaches the sink through the
l10n.yaml(arb-dir+template-arb-file) branch.Suggested fix
Resolve the candidate path and reject anything that escapes the workspace folder (fall back to the document directory when there is no workspace folder). Legitimate in-project templates are unaffected. PR attached.
This was reported through Google's VRP and closed as out of scope for that program; filing here so the project can fix it directly.