/** * `componentId` → a file inside THIS project, or a refusal. THE one resolution, * shared by the analyzer and the writer. * * A `componentId` is `::`, minted by * `component-tagger.ts` into `data-ve-file` / `data-ve-line` / `data-ve-col` and * handed back verbatim by the inspector. It arrives here from * `JSON.parse(body)` on one of the three dev-server RPCs * (`visual-editor/index.ts`), which carry no auth, no token and no CSRF check — * their module header states the callers are same-origin, which is a statement * about who is EXPECTED to call, not a check. Anything that can POST to the * preview origin — a script on the previewed page, generated app code, a * third-party embed, anything that can reach the pod's preview URL — can drive * them. * * ☠️ **`path.join(rootDir, relativePath)` is not a bound, and it was the only * thing standing between that body and the pod's filesystem.** `join` NORMALISES * `..` rather than refusing it, so `path.join('/workspace/repo', * '../../etc/hosts')` is `/etc/hosts`. Both readers did exactly that and nothing * else — no containment re-assert, no extension check — so the writers' * `writeFile(filePath, …)` (`ast-transformer.ts`, the update and delete paths) * would rewrite ANY module-parseable file the pod can write, and the analyzer * would read any file at all and answer with its parse error or its real JSX * values. * * The producer never needs the escape, which is what makes the bound free: * `component-tagger.ts` emits `path.relative(cwd, id)` for a file it PROCESSED, * and it processes only {@link TAGGABLE_EXTENSIONS}. So a legitimate * `componentId` always names a `.jsx`/`.tsx` file inside the project, and * refusing everything else costs nothing the visual editor can do. * * The check is LEXICAL (resolve, then re-assert the prefix) rather than a * `realpath` comparison: `realpath` needs the file to exist, which turns a * refusal into a probe, and the defect being closed is `..` arithmetic rather * than a symlink planted inside the project by whoever already owns the pod. */ /** * The extensions `component-tagger.ts` tags — its own `validExtensions`. A * coordinate can only ever point INTO a file it processed, so this is the exact * set a `componentId` may name. */ export declare const TAGGABLE_EXTENSIONS: ReadonlySet; export type ComponentIdResolution = { ok: true; filePath: string; relativePath: string; line: number; col: number; } | { ok: false; error: string; }; /** * Resolve `componentId` against `rootDir`, refusing anything that does not name * a taggable source file inside it. * * One error string per refusal REASON rather than per call site: the two readers * differ only in the envelope they wrap it in, and a second copy of the wording * is a second thing to keep in step. */ export declare function resolveComponentId(componentId: string, rootDir: string): ComponentIdResolution; //# sourceMappingURL=componentId.d.ts.map