@agent-pattern-labs/iso-index
Advanced tools
+35
-7
@@ -1,9 +0,13 @@ | ||
| import { existsSync, readdirSync, statSync } from "node:fs"; | ||
| import { dirname, join, relative, resolve, sep } from "node:path"; | ||
| import { existsSync, lstatSync, readdirSync } from "node:fs"; | ||
| import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"; | ||
| export function findMatchingFiles(root, include, exclude = []) { | ||
| const normalizedRoot = resolve(root); | ||
| const excludeRegexes = exclude.map(globToRegExp); | ||
| const seen = new Set(); | ||
| for (const pattern of include) { | ||
| const safeExcludes = exclude.map(validatePattern); | ||
| const excludeRegexes = safeExcludes.map(globToRegExp); | ||
| for (const rawPattern of include) { | ||
| const pattern = validatePattern(rawPattern); | ||
| const base = globBase(normalizedRoot, pattern); | ||
| if (!isWithinRoot(normalizedRoot, base)) | ||
| throw new Error(`glob pattern escapes root: ${rawPattern}`); | ||
| if (!existsSync(base)) | ||
@@ -22,3 +26,5 @@ continue; | ||
| function listFiles(path) { | ||
| const stat = statSync(path); | ||
| const stat = lstatSync(path); | ||
| if (stat.isSymbolicLink()) | ||
| return []; | ||
| if (stat.isFile()) | ||
@@ -45,2 +51,18 @@ return [path]; | ||
| } | ||
| function validatePattern(pattern) { | ||
| if (!pattern) | ||
| throw new Error("glob pattern must not be empty"); | ||
| const normalized = toPosix(pattern); | ||
| if (isAbsolute(pattern) || normalized.startsWith("/") || /^[A-Za-z]:/.test(normalized)) { | ||
| throw new Error(`glob pattern must be relative to root: ${pattern}`); | ||
| } | ||
| if (normalized.split("/").some((part) => part === "..")) { | ||
| throw new Error(`glob pattern escapes root: ${pattern}`); | ||
| } | ||
| return normalized.startsWith("./") ? normalized.slice(2) : normalized; | ||
| } | ||
| function isWithinRoot(root, target) { | ||
| const rel = relative(root, target); | ||
| return rel === "" || (!isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${sep}`)); | ||
| } | ||
| function globToRegExp(pattern) { | ||
@@ -53,4 +75,10 @@ const input = toPosix(pattern); | ||
| if (char === "*" && next === "*") { | ||
| out += ".*"; | ||
| i++; | ||
| if (input[i + 2] === "/") { | ||
| out += "(?:.*/)?"; | ||
| i += 2; | ||
| } | ||
| else { | ||
| out += ".*"; | ||
| i++; | ||
| } | ||
| } | ||
@@ -57,0 +85,0 @@ else if (char === "*") { |
+1
-1
| { | ||
| "name": "@agent-pattern-labs/iso-index", | ||
| "version": "0.1.1", | ||
| "version": "0.1.2", | ||
| "description": "Deterministic local artifact index for AI-agent workflows: build, query, and verify where authoritative facts live without model calls.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+5
-0
@@ -53,2 +53,7 @@ # @agent-pattern-labs/iso-index | ||
| Include and exclude globs are relative to `--root`; absolute, drive-prefixed, | ||
| and parent (`..`) patterns are rejected. The resolved search base is checked | ||
| again against the root, and directory symlinks are not followed. `**/` matches | ||
| zero or more directories. | ||
| ```json | ||
@@ -55,0 +60,0 @@ { |
47425
3.12%1090
2.64%121
4.31%