feat(governance): second wave enforcement - 64% improvement (28% → 46%)
Implements 7 additional architectural enforcement mechanisms: ✅ Prohibited Terms Detection (inst_016/017/018): - scripts/check-prohibited-terms.js - Scans for absolute assurance terms ("guarantee", "100% secure") - Detects maturity claims without evidence ("production-ready", "battle-tested") - Checks statistics require citation or [NEEDS VERIFICATION] - Integrated into .git/hooks/pre-commit (Check 2) ✅ Credential Exposure Prevention (inst_069/070): - scripts/check-credential-exposure.js - Detects real API keys, secrets, passwords in documentation - Validates example credentials use proper patterns (EXAMPLE/REDACTED) - CRITICAL: Runs first in pre-commit (Check 0) ✅ Confidential Document Protection (inst_012/015): - scripts/check-confidential-docs.js - Prevents deployment of internal/session-handoff documents - Scans filenames and content for [CONFIDENTIAL]/[INTERNAL] markers - Integrated into scripts/deploy.sh pre-flight checks ✅ Enhanced Pre-Commit Hook: Now runs 4 checks in order: 0. Credential exposure (CRITICAL) 1. CSP compliance 2. Prohibited terms 3. Test requirements ✅ Enhanced Deployment Script: - Added confidential document check to deploy.sh - Scans public/ and docs/ before deployment - Blocks deployment if confidential markers found ✅ Updated Enforcement Map: - Added all new mechanisms to audit-enforcement.js - Updated inst_008_CONSOLIDATED mapping - New mappings: inst_012, inst_015, inst_016, inst_017, inst_018, inst_069, inst_070 📊 Enforcement Progress: - Wave 1: 11/39 imperative instructions enforced (28%) - Wave 2: 18/39 imperative instructions enforced (46%) - Improvement: +7 instructions = +64% increase - Remaining gaps: 21/39 (54%) 🎯 Next Priority Gaps: - inst_013/043/045: API security validation - inst_019: Context pressure comprehensive accounting - inst_025: Deployment file mapping - inst_039/040: Batch operation verification - inst_079/080/081: Values/principles (process-based) 🔒 Security Posture: - CRITICAL security checks now run first (credential exposure) - All text files scanned before commit - All deployment candidates scanned before rsync 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
86d7042f42
commit
e7efdc7810
5 changed files with 482 additions and 1 deletions
|
|
@ -14,6 +14,12 @@ const INSTRUCTION_FILE = path.join(__dirname, '../.claude/instruction-history.js
|
|||
// Known enforcement mechanisms
|
||||
const ENFORCEMENT_MAP = {
|
||||
inst_008: ['.git/hooks/pre-commit', 'scripts/check-csp-violations.js'],
|
||||
inst_008_CONSOLIDATED: ['.git/hooks/pre-commit', 'scripts/check-csp-violations.js'],
|
||||
inst_012: ['scripts/check-confidential-docs.js', 'scripts/deploy.sh'],
|
||||
inst_015: ['scripts/check-confidential-docs.js', 'scripts/deploy.sh'],
|
||||
inst_016: ['scripts/check-prohibited-terms.js', '.git/hooks/pre-commit'],
|
||||
inst_017: ['scripts/check-prohibited-terms.js', '.git/hooks/pre-commit'],
|
||||
inst_018: ['scripts/check-prohibited-terms.js', '.git/hooks/pre-commit'],
|
||||
inst_023: ['scripts/track-background-process.js', 'scripts/session-init.js', 'scripts/session-closedown.js'],
|
||||
inst_027: ['.claude/hooks/framework-audit-hook.js'],
|
||||
inst_038: ['.claude/hooks/framework-audit-hook.js'],
|
||||
|
|
@ -22,7 +28,8 @@ const ENFORCEMENT_MAP = {
|
|||
inst_065: ['scripts/session-init.js'],
|
||||
inst_066: ['.git/hooks/commit-msg'],
|
||||
inst_068: ['.git/hooks/pre-commit'],
|
||||
inst_070: ['.git/hooks/pre-commit'],
|
||||
inst_069: ['scripts/check-credential-exposure.js', '.git/hooks/pre-commit'],
|
||||
inst_070: ['scripts/check-credential-exposure.js', '.git/hooks/pre-commit'],
|
||||
inst_071: ['scripts/deploy.sh'],
|
||||
inst_075: ['.claude/hooks/check-token-checkpoint.js'],
|
||||
inst_077: ['scripts/session-closedown.js'],
|
||||
|
|
|
|||
153
scripts/check-confidential-docs.js
Executable file
153
scripts/check-confidential-docs.js
Executable file
|
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Confidential Document Scanner - Enforces inst_012, inst_015
|
||||
* Prevents deployment of internal/confidential documents
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// File patterns that indicate confidential/internal documents
|
||||
const CONFIDENTIAL_PATTERNS = [
|
||||
/session[-_]?handoff/i,
|
||||
/phase[-_]?planning/i,
|
||||
/cost[-_]?estimate/i,
|
||||
/infrastructure[-_]?plan/i,
|
||||
/progress[-_]?report/i,
|
||||
/cover[-_]?letter/i,
|
||||
/testing[-_]?checklist/i,
|
||||
/internal/i,
|
||||
/confidential/i,
|
||||
/private/i,
|
||||
/draft/i,
|
||||
/wip[-_]/i, // work in progress
|
||||
/todo/i
|
||||
];
|
||||
|
||||
// Content markers that indicate confidential information
|
||||
const CONFIDENTIAL_CONTENT_MARKERS = [
|
||||
/\[INTERNAL\]/i,
|
||||
/\[CONFIDENTIAL\]/i,
|
||||
/\[DRAFT\]/i,
|
||||
/\[DO NOT PUBLISH\]/i,
|
||||
/\[WIP\]/i,
|
||||
/CONFIDENTIAL:/i,
|
||||
/INTERNAL ONLY:/i
|
||||
];
|
||||
|
||||
function checkFilePath(filePath) {
|
||||
const basename = path.basename(filePath);
|
||||
|
||||
for (const pattern of CONFIDENTIAL_PATTERNS) {
|
||||
if (pattern.test(basename) || pattern.test(filePath)) {
|
||||
return {
|
||||
confidential: true,
|
||||
reason: `Filename matches confidential pattern: ${pattern.source}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { confidential: false };
|
||||
}
|
||||
|
||||
function checkFileContent(filePath) {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
for (let i = 0; i < Math.min(20, lines.length); i++) {
|
||||
for (const marker of CONFIDENTIAL_CONTENT_MARKERS) {
|
||||
if (marker.test(lines[i])) {
|
||||
return {
|
||||
confidential: true,
|
||||
reason: `Content contains confidential marker at line ${i+1}: ${marker.source}`,
|
||||
line: i + 1,
|
||||
text: lines[i].trim()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { confidential: false };
|
||||
} catch (err) {
|
||||
// Can't read file (binary, etc.) - check by path only
|
||||
return { confidential: false };
|
||||
}
|
||||
}
|
||||
|
||||
function scanFile(filePath) {
|
||||
// Skip non-document files
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
if (!['.md', '.txt', '.pdf', '.doc', '.docx', '.html'].includes(ext)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check filename
|
||||
const pathCheck = checkFilePath(filePath);
|
||||
if (pathCheck.confidential) {
|
||||
return { file: filePath, ...pathCheck };
|
||||
}
|
||||
|
||||
// Check content
|
||||
const contentCheck = checkFileContent(filePath);
|
||||
if (contentCheck.confidential) {
|
||||
return { file: filePath, ...contentCheck };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
console.log('Usage: check-confidential-docs.js <file1> [file2] ...');
|
||||
console.log('');
|
||||
console.log('Scans files to prevent deployment of internal/confidential documents');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`\n🔍 Scanning ${args.length} file(s) for confidential markers...\n`);
|
||||
|
||||
const findings = [];
|
||||
|
||||
args.forEach(file => {
|
||||
if (!fs.existsSync(file)) return;
|
||||
|
||||
const result = scanFile(file);
|
||||
if (result) {
|
||||
findings.push(result);
|
||||
}
|
||||
});
|
||||
|
||||
if (findings.length === 0) {
|
||||
console.log('✅ No confidential documents detected\n');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Report findings
|
||||
console.log(`❌ Found ${findings.length} confidential document(s):\n`);
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
findings.forEach(f => {
|
||||
console.log(`🔴 ${f.file}`);
|
||||
console.log(` Reason: ${f.reason}`);
|
||||
if (f.text) {
|
||||
console.log(` Line ${f.line}: ${f.text.substring(0, 60)}...`);
|
||||
}
|
||||
console.log('');
|
||||
});
|
||||
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
console.log('⚠️ DEPLOYMENT BLOCKED (inst_012/inst_015)\n');
|
||||
console.log('These documents are marked confidential/internal.');
|
||||
console.log('');
|
||||
console.log('Actions:');
|
||||
console.log(' 1. Remove confidential markers if approved for public release');
|
||||
console.log(' 2. Move to a non-public directory');
|
||||
console.log(' 3. Get explicit human approval before deploying\n');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
main();
|
||||
136
scripts/check-credential-exposure.js
Executable file
136
scripts/check-credential-exposure.js
Executable file
|
|
@ -0,0 +1,136 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Credential Exposure Scanner - Enforces inst_069, inst_070
|
||||
* Detects real credentials in documentation and code
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// Patterns for real credentials (not example/redacted)
|
||||
const CREDENTIAL_PATTERNS = [
|
||||
// API keys that look real (not EXAMPLE/REDACTED)
|
||||
{ pattern: /sk-ant-api03-[A-Za-z0-9_-]{95,}(?!EXAMPLE|REDACTED)/g, type: 'Anthropic API Key' },
|
||||
{ pattern: /sk-[a-z0-9]{32,}(?!EXAMPLE|REDACTED|your-key)/gi, type: 'Stripe Secret Key' },
|
||||
{ pattern: /pk-[a-z0-9]{32,}(?!EXAMPLE|REDACTED|your-key)/gi, type: 'Stripe Public Key' },
|
||||
|
||||
// Generic patterns that look suspicious
|
||||
{ pattern: /api[_-]?key[\s:=]+["']?([a-z0-9]{32,})["']?(?!EXAMPLE|REDACTED|your-|xxx)/gi, type: 'Generic API Key' },
|
||||
{ pattern: /secret[\s:=]+["']?([a-z0-9]{32,})["']?(?!EXAMPLE|REDACTED|your-|xxx)/gi, type: 'Generic Secret' },
|
||||
{ pattern: /password[\s:=]+["']?([^"'\s]{8,})["']?(?!REDACTED|your-|xxx|example|password123)/gi, type: 'Possible Password' },
|
||||
|
||||
// AWS
|
||||
{ pattern: /AKIA[0-9A-Z]{16}(?!EXAMPLE)/g, type: 'AWS Access Key' },
|
||||
|
||||
// JWT tokens (look for proper structure, not examples)
|
||||
{ pattern: /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}(?!EXAMPLE)/g, type: 'JWT Token' },
|
||||
|
||||
// Database connection strings with real passwords
|
||||
{ pattern: /mongodb:\/\/[^:]+:([^@\s]{8,})@(?!REDACTED|your-password|example)/gi, type: 'MongoDB Credential' },
|
||||
{ pattern: /postgres:\/\/[^:]+:([^@\s]{8,})@(?!REDACTED|your-password|example)/gi, type: 'PostgreSQL Credential' }
|
||||
];
|
||||
|
||||
function checkFile(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
const findings = [];
|
||||
|
||||
lines.forEach((line, idx) => {
|
||||
CREDENTIAL_PATTERNS.forEach(({ pattern, type }) => {
|
||||
const matches = line.matchAll(pattern);
|
||||
for (const match of matches) {
|
||||
findings.push({
|
||||
file: filePath,
|
||||
line: idx + 1,
|
||||
type,
|
||||
text: line.trim(),
|
||||
match: match[0]
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return findings;
|
||||
}
|
||||
|
||||
function scanFiles(files) {
|
||||
const allFindings = [];
|
||||
|
||||
files.forEach(file => {
|
||||
if (!fs.existsSync(file)) return;
|
||||
|
||||
// Skip binary files, node_modules, etc.
|
||||
if (file.includes('node_modules') || file.includes('.git/') || file.includes('dist/')) return;
|
||||
|
||||
const ext = path.extname(file).toLowerCase();
|
||||
if (['.png', '.jpg', '.jpeg', '.gif', '.pdf', '.zip'].includes(ext)) return;
|
||||
|
||||
try {
|
||||
const findings = checkFile(file);
|
||||
allFindings.push(...findings);
|
||||
} catch (err) {
|
||||
// Binary file or encoding issue - skip
|
||||
}
|
||||
});
|
||||
|
||||
return allFindings;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
let files = [];
|
||||
if (args.length === 0) {
|
||||
// Scan staged git files
|
||||
try {
|
||||
const staged = execSync('git diff --cached --name-only --diff-filter=ACM', {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
files = staged.trim().split('\n').filter(f => f.length > 0);
|
||||
} catch (err) {
|
||||
console.log('⚠️ Not in git repository - skipping credential scan');
|
||||
process.exit(0);
|
||||
}
|
||||
} else {
|
||||
files = args;
|
||||
}
|
||||
|
||||
if (files.length === 0) {
|
||||
console.log('✅ No files to scan for credentials');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`\n🔍 Scanning ${files.length} file(s) for credential exposure...\n`);
|
||||
|
||||
const findings = scanFiles(files);
|
||||
|
||||
if (findings.length === 0) {
|
||||
console.log('✅ No credentials detected\n');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Report findings
|
||||
console.log(`❌ Found ${findings.length} potential credential(s):\n`);
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
findings.forEach(f => {
|
||||
console.log(`🔴 ${f.file}:${f.line}`);
|
||||
console.log(` Type: ${f.type}`);
|
||||
console.log(` Match: ${f.match.substring(0, 50)}${f.match.length > 50 ? '...' : ''}`);
|
||||
console.log(` Line: ${f.text.substring(0, 80)}${f.text.length > 80 ? '...' : ''}`);
|
||||
console.log('');
|
||||
});
|
||||
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
console.log('⚠️ CRITICAL SECURITY ISSUE (inst_069/inst_070)\n');
|
||||
console.log('Replace real credentials with:');
|
||||
console.log(' • API keys: "sk-ant-api03-EXAMPLE-REDACTED-NEVER-USE"');
|
||||
console.log(' • Secrets: "REDACTED" or "your-secret-here"');
|
||||
console.log(' • Passwords: "your-password-here"\n');
|
||||
console.log('Use environment variables or secret management for real credentials.\n');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
main();
|
||||
167
scripts/check-prohibited-terms.js
Executable file
167
scripts/check-prohibited-terms.js
Executable file
|
|
@ -0,0 +1,167 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Prohibited Terms Scanner - Enforces inst_016, inst_017, inst_018
|
||||
*
|
||||
* Scans files for prohibited language that violates governance rules
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// inst_017: Prohibited absolute assurance terms
|
||||
const PROHIBITED_ABSOLUTE_TERMS = [
|
||||
/\bguarantee(s|d)?\b/i,
|
||||
/\bensures?\s+(100%|complete|total|absolute)/i,
|
||||
/\beliminates?\s+all\b/i,
|
||||
/\bcompletely\s+(prevents?|eliminates?|removes?)\b/i,
|
||||
/\bnever\s+fails?\b/i,
|
||||
/\b100%\s+(safe|secure|reliable|accurate)\b/i,
|
||||
/\babsolutely\s+(prevents?|guarantees?)\b/i
|
||||
];
|
||||
|
||||
// inst_018: Prohibited maturity claims without evidence
|
||||
const PROHIBITED_MATURITY_CLAIMS = [
|
||||
/\b(production-ready|battle-tested|enterprise-proven)\b/i,
|
||||
/\bvalidated\s+by\s+\d+\s+(companies|organizations|teams)\b/i,
|
||||
/\bwidely\s+adopted\b/i,
|
||||
/\bmarket\s+(leader|validated)\b/i,
|
||||
/\bcustomer\s+base\s+of\b/i
|
||||
];
|
||||
|
||||
// inst_016: Requires citation or [NEEDS VERIFICATION]
|
||||
const STATS_PATTERNS = [
|
||||
/\d+%\s+(improvement|increase|reduction|faster|better)/i,
|
||||
/\d+x\s+(faster|better|more)/i,
|
||||
/ROI\s+of\s+\d+/i,
|
||||
/reduces?\s+(cost|time|effort)\s+by\s+\d+/i
|
||||
];
|
||||
|
||||
function checkFile(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
const violations = [];
|
||||
|
||||
lines.forEach((line, idx) => {
|
||||
const lineNum = idx + 1;
|
||||
|
||||
// Check inst_017: Absolute assurance terms
|
||||
PROHIBITED_ABSOLUTE_TERMS.forEach(pattern => {
|
||||
if (pattern.test(line)) {
|
||||
violations.push({
|
||||
file: filePath,
|
||||
line: lineNum,
|
||||
type: 'inst_017',
|
||||
severity: 'HIGH',
|
||||
text: line.trim(),
|
||||
rule: 'Prohibited absolute assurance term detected'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Check inst_018: Maturity claims
|
||||
PROHIBITED_MATURITY_CLAIMS.forEach(pattern => {
|
||||
if (pattern.test(line)) {
|
||||
violations.push({
|
||||
file: filePath,
|
||||
line: lineNum,
|
||||
type: 'inst_018',
|
||||
severity: 'HIGH',
|
||||
text: line.trim(),
|
||||
rule: 'Prohibited maturity claim without evidence'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Check inst_016: Statistics without citation
|
||||
STATS_PATTERNS.forEach(pattern => {
|
||||
if (pattern.test(line)) {
|
||||
// Check if line has citation or [NEEDS VERIFICATION]
|
||||
if (!line.includes('[') && !line.includes('(source:') && !line.includes('Citation:')) {
|
||||
violations.push({
|
||||
file: filePath,
|
||||
line: lineNum,
|
||||
type: 'inst_016',
|
||||
severity: 'MEDIUM',
|
||||
text: line.trim(),
|
||||
rule: 'Statistic without citation or [NEEDS VERIFICATION] marker'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return violations;
|
||||
}
|
||||
|
||||
function scanFiles(files) {
|
||||
const allViolations = [];
|
||||
|
||||
files.forEach(file => {
|
||||
if (!fs.existsSync(file)) return;
|
||||
|
||||
// Only scan text files (markdown, HTML, text)
|
||||
const ext = path.extname(file).toLowerCase();
|
||||
if (!['.md', '.html', '.txt', '.json'].includes(ext)) return;
|
||||
|
||||
const violations = checkFile(file);
|
||||
allViolations.push(...violations);
|
||||
});
|
||||
|
||||
return allViolations;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
let files = [];
|
||||
if (args.length === 0) {
|
||||
// Scan staged git files
|
||||
try {
|
||||
const staged = execSync('git diff --cached --name-only --diff-filter=ACM', {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
files = staged.trim().split('\n').filter(f => f.length > 0);
|
||||
} catch (err) {
|
||||
console.error('Not in a git repository or no staged files');
|
||||
process.exit(0);
|
||||
}
|
||||
} else {
|
||||
files = args;
|
||||
}
|
||||
|
||||
if (files.length === 0) {
|
||||
console.log('✅ No files to scan');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`\n🔍 Scanning ${files.length} file(s) for prohibited terms...\n`);
|
||||
|
||||
const violations = scanFiles(files);
|
||||
|
||||
if (violations.length === 0) {
|
||||
console.log('✅ No prohibited terms detected\n');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Report violations
|
||||
console.log(`❌ Found ${violations.length} violation(s):\n`);
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
violations.forEach(v => {
|
||||
console.log(`${v.severity === 'HIGH' ? '🔴' : '🟡'} ${v.file}:${v.line}`);
|
||||
console.log(` Rule: ${v.type} - ${v.rule}`);
|
||||
console.log(` Text: ${v.text.substring(0, 80)}${v.text.length > 80 ? '...' : ''}`);
|
||||
console.log('');
|
||||
});
|
||||
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
console.log('Fix violations before committing/deploying:\n');
|
||||
console.log(' inst_016: Add citation or [NEEDS VERIFICATION] to statistics');
|
||||
console.log(' inst_017: Replace absolute terms with evidence-based language');
|
||||
console.log(' inst_018: Remove maturity claims or add documented evidence\n');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -109,6 +109,24 @@ if [ "$FRONTEND_ONLY" = false ]; then
|
|||
echo -e " ✓ .rsyncignore found"
|
||||
fi
|
||||
|
||||
# Check for confidential documents (inst_012/inst_015)
|
||||
echo -e " Checking for confidential documents..."
|
||||
if [ "$FRONTEND_ONLY" = true ]; then
|
||||
PUBLIC_FILES=$(find public -type f \( -name "*.md" -o -name "*.html" -o -name "*.txt" \) 2>/dev/null || true)
|
||||
else
|
||||
PUBLIC_FILES=$(find public docs -type f \( -name "*.md" -o -name "*.html" -o -name "*.txt" \) 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
if [ -n "$PUBLIC_FILES" ]; then
|
||||
if ! node scripts/check-confidential-docs.js $PUBLIC_FILES 2>&1 | grep -q "No confidential"; then
|
||||
echo -e "${RED}✗ ERROR: Confidential documents detected - DEPLOYMENT BLOCKED (inst_012/inst_015)${NC}"
|
||||
echo ""
|
||||
node scripts/check-confidential-docs.js $PUBLIC_FILES
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo -e " ✓ No confidential documents"
|
||||
|
||||
# Check local server is running
|
||||
if ! lsof -i :9000 >/dev/null 2>&1; then
|
||||
echo -e "${YELLOW} ⚠ WARNING: Local server not running on port 9000${NC}"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue