mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-28 07:16:53 +05:00
refactor: migrate hardcode detector from regex to AST-based analysis
- Replace regex-based matchers with tree-sitter AST traversal - Add duplicate value tracking across files - Implement boolean literal detection - Add value type classification (email, url, ip, api_key, etc.) - Improve context awareness with AST node analysis - Reduce false positives with better constant detection Breaking changes removed: - BraceTracker.ts - ExportConstantAnalyzer.ts - MagicNumberMatcher.ts - MagicStringMatcher.ts New components added: - AstTreeTraverser for AST walking - DuplicateValueTracker for cross-file tracking - AstContextChecker for node context analysis - AstNumberAnalyzer, AstStringAnalyzer, AstBooleanAnalyzer - ValuePatternMatcher for type detection Test coverage: 87.97% statements, 96.75% functions
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import Parser from "tree-sitter"
|
||||
import { HardcodedValue, HardcodeType } from "../../domain/value-objects/HardcodedValue"
|
||||
import { HARDCODE_TYPES } from "../../shared/constants/rules"
|
||||
import { ALLOWED_NUMBERS } from "../constants/defaults"
|
||||
import { AstContextChecker } from "./AstContextChecker"
|
||||
|
||||
/**
|
||||
* AST-based analyzer for detecting configuration objects with hardcoded values
|
||||
*
|
||||
* Detects objects that contain multiple hardcoded values that should be
|
||||
* extracted to a configuration file.
|
||||
*
|
||||
* Example:
|
||||
* const config = { timeout: 5000, retries: 3, url: "http://..." }
|
||||
*/
|
||||
export class AstConfigObjectAnalyzer {
|
||||
private readonly MIN_HARDCODED_VALUES = 2
|
||||
|
||||
constructor(private readonly contextChecker: AstContextChecker) {}
|
||||
|
||||
/**
|
||||
* Analyzes an object expression and returns a violation if it contains many hardcoded values
|
||||
*/
|
||||
public analyze(node: Parser.SyntaxNode, lines: string[]): HardcodedValue | null {
|
||||
if (node.type !== "object") {
|
||||
return null
|
||||
}
|
||||
|
||||
if (this.contextChecker.isInExportedConstant(node)) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (this.contextChecker.isInTypeContext(node)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const hardcodedCount = this.countHardcodedValues(node)
|
||||
|
||||
if (hardcodedCount < this.MIN_HARDCODED_VALUES) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.createViolation(node, hardcodedCount, lines)
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts hardcoded values in an object
|
||||
*/
|
||||
private countHardcodedValues(objectNode: Parser.SyntaxNode): number {
|
||||
let count = 0
|
||||
|
||||
for (const child of objectNode.children) {
|
||||
if (child.type === "pair") {
|
||||
const value = child.childForFieldName("value")
|
||||
if (value && this.isHardcodedValue(value)) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a node is a hardcoded value
|
||||
*/
|
||||
private isHardcodedValue(node: Parser.SyntaxNode): boolean {
|
||||
if (node.type === "number") {
|
||||
const value = parseInt(node.text, 10)
|
||||
return !ALLOWED_NUMBERS.has(value) && value >= 100
|
||||
}
|
||||
|
||||
if (node.type === "string") {
|
||||
const stringFragment = node.children.find((c) => c.type === "string_fragment")
|
||||
return stringFragment !== undefined && stringFragment.text.length > 3
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HardcodedValue violation for a config object
|
||||
*/
|
||||
private createViolation(
|
||||
node: Parser.SyntaxNode,
|
||||
hardcodedCount: number,
|
||||
lines: string[],
|
||||
): HardcodedValue {
|
||||
const lineNumber = node.startPosition.row + 1
|
||||
const column = node.startPosition.column
|
||||
const context = lines[node.startPosition.row]?.trim() ?? ""
|
||||
|
||||
const objectPreview = this.getObjectPreview(node)
|
||||
|
||||
return HardcodedValue.create(
|
||||
`Configuration object with ${String(hardcodedCount)} hardcoded values: ${objectPreview}`,
|
||||
HARDCODE_TYPES.MAGIC_CONFIG as HardcodeType,
|
||||
lineNumber,
|
||||
column,
|
||||
context,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a preview of the object for the violation message
|
||||
*/
|
||||
private getObjectPreview(node: Parser.SyntaxNode): string {
|
||||
const text = node.text
|
||||
if (text.length <= 50) {
|
||||
return text
|
||||
}
|
||||
return `${text.substring(0, 47)}...`
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user