Perform code optimization and document cleanup (#1)
## Summary - add repository-wide quality tooling and verification scaffolding, including CI workflows, pnpm workspace setup, ESLint/Prettier/markdown checks, and generated-output verification helpers - reorganize skill sources and generation flow by introducing canonical `_source` variants, generator/manifests, reusable helper abstractions, and shared web-automation/browser utilities - clean up and expand documentation so the root README flows into docs and skill docs, with clearer development, reviewer, installer, and workflow guidance ## Notable changes - docs flow and consistency cleanup across `README.md`, `docs/README.md`, and related docs - new scripts for `check`, docs verification, generated-file verification, shell portability, and safe directory replacement - refactors in Atlassian and web-automation skill runtimes to reduce duplication and centralize reusable code - changelog, development documentation, and CI surface updates ## Test Plan - [ ] `pnpm run check` - [ ] review generated/manifests and skill sync outputs - [ ] smoke-check docs flow from `README.md` to `docs/README.md` to skill docs ## Notes - this branch currently includes tracked `skills/web-automation/shared/node_modules` content that should be reviewed carefully as potentially noisy/accidental committed artifacts Co-authored-by: Stefano Fiorini <stefano.fiorini@firsthorizon.com> Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* safe-replace-dir.mjs — safely replace a directory within a safety-root boundary
|
||||
*
|
||||
* Exports:
|
||||
* safeReplaceDir(source, target, safetyRoot) → Promise<void>
|
||||
*
|
||||
* Usage:
|
||||
* import { safeReplaceDir } from "./lib/safe-replace-dir.mjs";
|
||||
* await safeReplaceDir("/path/to/source", "/safe/root/target", "/safe/root");
|
||||
*
|
||||
* Safety contract:
|
||||
* - `target` must be a strict descendant of `safetyRoot` (not equal to it).
|
||||
* - `target` must be a non-empty path.
|
||||
* - Throws with a descriptive message if either constraint is violated.
|
||||
*
|
||||
* Behaviour:
|
||||
* - Removes any existing content at `target` (rm -rf equivalent).
|
||||
* - Creates `target` (and any missing parent directories).
|
||||
* - Copies all files from `source` into `target`.
|
||||
*/
|
||||
|
||||
import { cp, mkdir, realpath, rm } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
/**
|
||||
* Safely replace `target` with the contents of `source`, enforcing that
|
||||
* `target` is a strict descendant of `safetyRoot`.
|
||||
*
|
||||
* @param {string} source - Directory to copy from.
|
||||
* @param {string} target - Directory to replace (will be removed then recreated).
|
||||
* @param {string} safetyRoot - Ancestor boundary; `target` must be inside this.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function safeReplaceDir(source, target, safetyRoot) {
|
||||
if (!target || target === "") {
|
||||
throw new Error(`Refusing to replace unsafe target: (empty string)`);
|
||||
}
|
||||
|
||||
const resolvedSafety = path.resolve(safetyRoot);
|
||||
const resolvedTarget = path.resolve(target);
|
||||
|
||||
// Lexical check: target must be a strict descendant of safetyRoot.
|
||||
const relative = path.relative(resolvedSafety, resolvedTarget);
|
||||
if (!relative || relative.startsWith("..") || path.isAbsolute(relative) || relative === "") {
|
||||
throw new Error(`Refusing to replace target outside safety root: ${target}`);
|
||||
}
|
||||
|
||||
// Real-path check: resolve the deepest existing ancestor of target's parent
|
||||
// and verify it lies inside the real (symlink-resolved) safety root.
|
||||
// This blocks a symlinked parent directory from redirecting outside the boundary.
|
||||
const realSafety = await realpath(resolvedSafety);
|
||||
let checkPath = path.dirname(resolvedTarget);
|
||||
for (;;) {
|
||||
try {
|
||||
const realAncestor = await realpath(checkPath);
|
||||
const realRel = path.relative(realSafety, realAncestor);
|
||||
if (realRel.startsWith("..") || path.isAbsolute(realRel)) {
|
||||
throw new Error(`Refusing to replace target outside safety root: ${target}`);
|
||||
}
|
||||
break; // validation passed
|
||||
} catch (err) {
|
||||
if (err.code === "ENOENT") {
|
||||
const parent = path.dirname(checkPath);
|
||||
if (parent === checkPath) {
|
||||
throw new Error(`Refusing to replace target outside safety root: ${target}`, { cause: err });
|
||||
}
|
||||
checkPath = parent;
|
||||
continue;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
await rm(resolvedTarget, { recursive: true, force: true });
|
||||
await mkdir(resolvedTarget, { recursive: true });
|
||||
await cp(source, resolvedTarget, { recursive: true, force: true });
|
||||
}
|
||||
Reference in New Issue
Block a user