@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
JavaScript
"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