tractatus/scripts/hook-validators/validate-credentials.js
TheFlow 5b947e3b6f chore(framework): update instruction history and hook metrics
Update framework tracking files from extended session work:
- Instruction history with security workflow instructions
- Hook metrics from document security session
- Hook validator updates for pre-action checks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 12:48:21 +13:00

219 lines
6.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Credential & Attribution Validator
*
* Prevents unauthorized changes to:
* - Copyright attribution
* - GitHub repository URLs
* - Contact emails
* - Domain names
* - API keys/credentials
* - Legal entity names
*
* Architectural enforcement to prevent Claude from arbitrarily changing
* important credentials without human confirmation.
*
* Created: 2025-10-18
* Incident: Footer component overwrote copyright from "John G Stroh" to "Tractatus AI Safety Framework"
*/
const fs = require('fs');
const path = require('path');
// IMMUTABLE CREDENTIALS - These values MUST NOT be changed without human approval
const PROTECTED_VALUES = {
copyright_holder: 'John G Stroh',
github_org: 'AgenticGovernance',
github_repo: 'tractatus-framework',
primary_email: 'hello@agenticgovernance.digital',
support_email: 'support@agenticgovernance.digital',
domain: 'agenticgovernance.digital',
license: 'Apache License 2.0',
};
// File patterns that commonly contain credentials
const CREDENTIAL_FILES = [
'public/js/components/footer.js',
'LICENSE',
'package.json',
'public/**/*.html',
'docs/**/*.md',
];
/**
* Validate that protected values haven't been arbitrarily changed
*/
function validateCredentials(filePath, fileContent) {
const violations = [];
// Check for incorrect copyright attribution
const copyrightRegex = /©.*?(\d{4})\s+([^.\n<]+)/g;
let match;
while ((match = copyrightRegex.exec(fileContent)) !== null) {
const holder = match[2].trim();
// Allow "John G Stroh" or "John G. Stroh" (with period)
if (!holder.includes('John G') && !holder.includes(PROTECTED_VALUES.copyright_holder)) {
violations.push({
type: 'COPYRIGHT_MISMATCH',
line: getLineNumber(fileContent, match.index),
found: holder,
expected: PROTECTED_VALUES.copyright_holder,
message: `Copyright holder must be "${PROTECTED_VALUES.copyright_holder}" (from LICENSE file)`
});
}
}
// Check for incorrect GitHub URLs
const githubRegex = /github\.com\/([^\/\s"']+)\/([^\/\s"'<]+)/g;
while ((match = githubRegex.exec(fileContent)) !== null) {
const org = match[1];
const repo = match[2];
// Check if it's supposed to be our repo but has wrong values
if ((org !== PROTECTED_VALUES.github_org || repo !== PROTECTED_VALUES.github_repo) &&
(org.includes('tractatus') || repo.includes('tractatus'))) {
violations.push({
type: 'GITHUB_URL_INCORRECT',
line: getLineNumber(fileContent, match.index),
found: `${org}/${repo}`,
expected: `${PROTECTED_VALUES.github_org}/${PROTECTED_VALUES.github_repo}`,
message: `GitHub repository must be "${PROTECTED_VALUES.github_org}/${PROTECTED_VALUES.github_repo}"`
});
}
}
// Check for placeholder GitHub URLs
if (fileContent.includes('github.com/yourusername') ||
fileContent.includes('github.com/your-org')) {
violations.push({
type: 'GITHUB_PLACEHOLDER',
message: 'GitHub URL contains placeholder - must use actual repository URL',
expected: `https://github.com/${PROTECTED_VALUES.github_org}/${PROTECTED_VALUES.github_repo}`
});
}
// Check for incorrect domain references
const domainRegex = /https?:\/\/([a-z0-9-]+\.)+[a-z]{2,}/gi;
while ((match = domainRegex.exec(fileContent)) !== null) {
const url = match[0];
// Skip CDNs, external services, etc.
if (url.includes('tractatus') && !url.includes(PROTECTED_VALUES.domain)) {
violations.push({
type: 'DOMAIN_INCORRECT',
line: getLineNumber(fileContent, match.index),
found: url,
expected: `https://${PROTECTED_VALUES.domain}`,
message: `Domain must be "${PROTECTED_VALUES.domain}"`
});
}
}
return violations;
}
/**
* Get line number from character index
*/
function getLineNumber(content, index) {
return content.substring(0, index).split('\n').length;
}
/**
* Main validation function
*/
function validate(filePath, operation) {
// Only validate files that might contain credentials
const shouldValidate = CREDENTIAL_FILES.some(pattern => {
if (pattern.includes('*')) {
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
return regex.test(filePath);
}
return filePath.includes(pattern);
});
if (!shouldValidate) {
return { valid: true };
}
// Read file content
let content;
try {
content = fs.readFileSync(filePath, 'utf-8');
} catch (error) {
// File doesn't exist yet (being created)
return { valid: true };
}
// Validate credentials
const violations = validateCredentials(filePath, content);
if (violations.length > 0) {
return {
valid: false,
violations,
message: formatViolations(filePath, violations)
};
}
return { valid: true };
}
/**
* Format violations for human-readable output
*/
function formatViolations(filePath, violations) {
let message = `\n❌ CREDENTIAL VIOLATION in ${filePath}\n\n`;
message += `PROTECTED CREDENTIALS ENFORCEMENT:\n`;
message += `The following values are IMMUTABLE and require human approval to change:\n\n`;
for (const violation of violations) {
message += ` ⚠️ ${violation.type}:\n`;
if (violation.line) {
message += ` Line: ${violation.line}\n`;
}
if (violation.found) {
message += ` Found: "${violation.found}"\n`;
}
message += ` Expected: "${violation.expected}"\n`;
message += ` Reason: ${violation.message}\n\n`;
}
message += `\n`;
message += `RESOLUTION:\n`;
message += `1. This change requires EXPLICIT human approval\n`;
message += `2. Verify the change is intentional and authorized\n`;
message += `3. If correcting an error, update to the protected values above\n`;
message += `4. If legitimately changing credentials, update PROTECTED_VALUES in:\n`;
message += ` scripts/hook-validators/validate-credentials.js\n`;
message += `\n`;
message += `Protected values are defined in LICENSE and package.json.\n`;
message += `Copyright holder: John G Stroh (LICENSE:189)\n`;
message += `GitHub repository: AgenticGovernance/tractatus-framework\n`;
return message;
}
// Export for use as module
module.exports = { validate, validateCredentials, PROTECTED_VALUES };
// CLI usage
if (require.main === module) {
const args = process.argv.slice(2);
if (args.length < 2) {
console.error('Usage: validate-credentials.js <file-path> <operation>');
process.exit(1);
}
const [filePath, operation] = args;
const result = validate(filePath, operation);
if (!result.valid) {
console.error(result.message);
process.exit(1);
}
process.exit(0);
}