mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-14 00:23:04 +08:00
fix: harden CI validators
Ports personal-path validator hardening and quoted checkout detection onto current main.
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Prevent shipping user-specific absolute paths in public docs/skills/commands.
|
||||
*
|
||||
* Catches generic `/Users/<name>` (macOS) and `C:\Users\<name>` (Windows) paths,
|
||||
* while allowing obvious placeholder usernames used in templates/examples.
|
||||
* Forensic incident reports under `docs/fixes/` are exempt because they may
|
||||
* legitimately document a reporter's local machine path.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -18,11 +23,50 @@ const TARGETS = [
|
||||
'.opencode/commands',
|
||||
];
|
||||
|
||||
const BLOCK_PATTERNS = [
|
||||
/\/Users\/affoon\b/g,
|
||||
/C:\\Users\\affoon\b/gi,
|
||||
const EXEMPT_PREFIXES = [
|
||||
'docs/fixes/',
|
||||
];
|
||||
|
||||
const PLACEHOLDER_USERNAMES = new Set([
|
||||
'example',
|
||||
'me',
|
||||
'user',
|
||||
'username',
|
||||
'you',
|
||||
'yourname',
|
||||
'yourusername',
|
||||
'your-username',
|
||||
]);
|
||||
|
||||
const POSIX_USER_RE = /\/Users\/([a-zA-Z][a-zA-Z0-9._-]*)/g;
|
||||
const WIN_USER_RE = /C:\\Users\\([a-zA-Z][a-zA-Z0-9._-]*)/gi;
|
||||
|
||||
function repoRelative(file) {
|
||||
return path.relative(ROOT, file).split(path.sep).join('/');
|
||||
}
|
||||
|
||||
function isExempt(file) {
|
||||
const rel = repoRelative(file);
|
||||
return EXEMPT_PREFIXES.some(prefix => rel.startsWith(prefix));
|
||||
}
|
||||
|
||||
function findLeaks(content) {
|
||||
const leaks = [];
|
||||
|
||||
for (const pattern of [POSIX_USER_RE, WIN_USER_RE]) {
|
||||
pattern.lastIndex = 0;
|
||||
let match;
|
||||
|
||||
while ((match = pattern.exec(content)) !== null) {
|
||||
if (!PLACEHOLDER_USERNAMES.has(match[1].toLowerCase())) {
|
||||
leaks.push(match[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return leaks;
|
||||
}
|
||||
|
||||
function collectFiles(targetPath, out) {
|
||||
if (!fs.existsSync(targetPath)) return;
|
||||
const stat = fs.statSync(targetPath);
|
||||
@@ -45,14 +89,14 @@ for (const target of TARGETS) {
|
||||
let failures = 0;
|
||||
for (const file of files) {
|
||||
if (!/\.(md|json|js|ts|sh|toml|yml|yaml)$/i.test(file)) continue;
|
||||
if (isExempt(file)) continue;
|
||||
|
||||
const content = fs.readFileSync(file, 'utf8');
|
||||
for (const pattern of BLOCK_PATTERNS) {
|
||||
const match = content.match(pattern);
|
||||
if (match) {
|
||||
console.error(`ERROR: personal path detected in ${path.relative(ROOT, file)}`);
|
||||
failures += match.length;
|
||||
break;
|
||||
}
|
||||
const leaks = findLeaks(content);
|
||||
|
||||
for (const leak of leaks) {
|
||||
console.error(`ERROR: personal path "${leak}" detected in ${repoRelative(file)}`);
|
||||
failures += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ function extractCheckoutSteps(source) {
|
||||
startLine: block.startLine,
|
||||
text: block.lines.join('\n'),
|
||||
}))
|
||||
.filter(block => /uses:\s*actions\/checkout@/m.test(block.text));
|
||||
.filter(block => /uses:\s*['"]?actions\/checkout@/m.test(block.text));
|
||||
}
|
||||
|
||||
function findViolations(filePath, source) {
|
||||
|
||||
Reference in New Issue
Block a user