UNPKG

@samiyev/guardian

Version:

Research-backed code quality guardian for AI-assisted development. Detects hardcodes, secrets, circular deps, framework leaks, entity exposure, and 9 architecture violations. Enforces Clean Architecture/DDD principles. Works with GitHub Copilot, Cursor, W

116 lines 4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AstStringAnalyzer = void 0; const HardcodedValue_1 = require("../../domain/value-objects/HardcodedValue"); const rules_1 = require("../../shared/constants/rules"); const ast_node_types_1 = require("../../shared/constants/ast-node-types"); const ValuePatternMatcher_1 = require("./ValuePatternMatcher"); /** * AST-based analyzer for detecting magic strings * * Analyzes string literal nodes in the AST to determine if they are * hardcoded values that should be extracted to constants. * * Detects various types of hardcoded strings: * - URLs and connection strings * - Email addresses * - IP addresses * - File paths * - Dates * - API keys */ class AstStringAnalyzer { contextChecker; patternMatcher; constructor(contextChecker) { this.contextChecker = contextChecker; this.patternMatcher = new ValuePatternMatcher_1.ValuePatternMatcher(); } /** * Analyzes a string node and returns a violation if it's a magic string */ analyze(node, lines) { const stringFragment = node.children.find((child) => child.type === ast_node_types_1.AST_STRING_TYPES.STRING_FRAGMENT); if (!stringFragment) { return null; } const value = stringFragment.text; if (value.length <= 3) { return null; } if (this.contextChecker.isInExportedConstant(node)) { return null; } if (this.contextChecker.isInTypeContext(node)) { return null; } if (this.contextChecker.isInImportStatement(node)) { return null; } if (this.contextChecker.isInTestDescription(node)) { return null; } if (this.contextChecker.isInConsoleCall(node)) { return null; } if (this.contextChecker.isInSymbolCall(node)) { return null; } if (this.contextChecker.isInTypeofCheck(node)) { return null; } if (this.shouldDetect(node, value)) { return this.createViolation(node, value, lines); } return null; } /** * Checks if string value should be detected */ shouldDetect(node, value) { if (this.patternMatcher.shouldDetect(value)) { return true; } if (this.hasConfigurationContext(node)) { return true; } return false; } /** * Checks if string is in a configuration-related context */ hasConfigurationContext(node) { const context = this.contextChecker.getNodeContext(node).toLowerCase(); const configKeywords = [ "url", "uri", ...rules_1.CONFIG_KEYWORDS.NETWORK, "api", ...rules_1.CONFIG_KEYWORDS.DATABASE, "db", "env", ...rules_1.CONFIG_KEYWORDS.SECURITY, "key", ...rules_1.CONFIG_KEYWORDS.MESSAGES, "label", ...rules_1.CONFIG_KEYWORDS.TECHNICAL, ]; return configKeywords.some((keyword) => context.includes(keyword)); } /** * Creates a HardcodedValue violation from a string node */ createViolation(node, value, lines) { const lineNumber = node.startPosition.row + 1; const column = node.startPosition.column; const context = lines[node.startPosition.row]?.trim() ?? ""; const detectedType = this.patternMatcher.detectType(value); const valueType = detectedType || (this.hasConfigurationContext(node) ? rules_1.DETECTION_VALUES.TYPE_CONFIG : rules_1.DETECTION_VALUES.TYPE_GENERIC); return HardcodedValue_1.HardcodedValue.create(value, rules_1.HARDCODE_TYPES.MAGIC_STRING, lineNumber, column, context, valueType); } } exports.AstStringAnalyzer = AstStringAnalyzer; //# sourceMappingURL=AstStringAnalyzer.js.map