@sun-asterisk/sunlint
Version:
☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards
243 lines (209 loc) • 7.54 kB
JavaScript
/**
* S044 Main Analyzer - Re-authentication Required for Sensitive Operations
* Primary: Symbol-based analysis (when available)
* Fallback: Regex-based for all other cases
* Command: node cli.js --rule=S044 --input=examples/rule-test-fixtures/rules/S044_re_authentication_required --engine=heuristic
*/
const S044SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
const S044RegexBasedAnalyzer = require("./regex-based-analyzer.js");
class S044Analyzer {
constructor(options = {}) {
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Constructor called with options:`, !!options);
console.log(
`🔧 [S044] Options type:`,
typeof options,
Object.keys(options || {})
);
}
this.ruleId = "S044";
this.ruleName = "Re-authentication Required for Sensitive Operations";
this.description =
"Require re-authentication before performing sensitive operations such as password changes, email changes, profile updates, and other critical account modifications. This prevents unauthorized access to sensitive account functions even if a session is compromised.";
this.semanticEngine = options.semanticEngine || null;
this.verbose = options.verbose || false;
// Configuration
this.config = {
useSymbolBased: true, // Primary approach
fallbackToRegex: true, // Secondary approach
regexBasedOnly: false, // Can be set to true for pure mode
};
// Initialize analyzers
try {
this.symbolAnalyzer = new S044SymbolBasedAnalyzer(this.semanticEngine);
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Symbol analyzer created successfully`);
}
} catch (error) {
console.error(`🔧 [S044] Error creating symbol analyzer:`, error);
}
try {
this.regexAnalyzer = new S044RegexBasedAnalyzer(this.semanticEngine);
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Regex analyzer created successfully`);
}
} catch (error) {
console.error(`🔧 [S044] Error creating regex analyzer:`, error);
}
}
/**
* Initialize analyzer with semantic engine
*/
async initialize(semanticEngine) {
this.semanticEngine = semanticEngine;
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Main analyzer initializing...`);
}
// Initialize both analyzers
if (this.symbolAnalyzer) {
await this.symbolAnalyzer.initialize?.(semanticEngine);
}
if (this.regexAnalyzer) {
await this.regexAnalyzer.initialize?.(semanticEngine);
}
// Clean up if needed
if (this.regexAnalyzer) {
this.regexAnalyzer.cleanup?.();
}
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Main analyzer initialized successfully`);
}
}
/**
* Single file analysis method for testing
*/
analyzeSingle(filePath, options = {}) {
if (process.env.SUNLINT_DEBUG) {
console.log(`📊 [S044] analyzeSingle() called for: ${filePath}`);
}
// Return result using same format as analyze method
return this.analyze([filePath], "typescript", options);
}
async analyze(files, language, options = {}) {
if (process.env.SUNLINT_DEBUG) {
console.log(
`🔧 [S044] analyze() method called with ${files.length} files, language: ${language}`
);
}
const violations = [];
for (const filePath of files) {
try {
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Processing file: ${filePath}`);
}
const fileViolations = await this.analyzeFile(filePath, options);
violations.push(...fileViolations);
if (process.env.SUNLINT_DEBUG) {
console.log(
`🔧 [S044] File ${filePath}: Found ${fileViolations.length} violations`
);
}
} catch (error) {
console.warn(
`⚠ [S044] Analysis failed for ${filePath}:`,
error.message
);
}
}
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Total violations found: ${violations.length}`);
}
return violations;
}
async analyzeFile(filePath, options = {}) {
if (process.env.SUNLINT_DEBUG) {
console.log(`🔍 [S044] analyzeFile() called for: ${filePath}`);
}
// Create a Map to track unique violations and prevent duplicates
const violationMap = new Map();
// 1. Try Symbol-based analysis first (primary)
if (
this.config.useSymbolBased &&
this.semanticEngine?.project &&
this.semanticEngine?.initialized
) {
try {
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Trying symbol-based analysis...`);
}
const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
if (sourceFile) {
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Source file found, analyzing...`);
}
const symbolViolations = await this.symbolAnalyzer.analyze(
sourceFile,
filePath
);
// Add to violation map with deduplication
symbolViolations.forEach((violation) => {
const key = `${violation.line}:${violation.column}:${violation.message}`;
if (!violationMap.has(key)) {
violationMap.set(key, violation);
}
});
if (process.env.SUNLINT_DEBUG) {
console.log(
`🔧 [S044] Symbol analysis completed: ${symbolViolations.length} violations`
);
}
} else {
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Source file not found, falling back...`);
}
}
} catch (error) {
console.warn(`⚠ [S044] Symbol analysis failed:`, error.message);
}
}
// 2. Try Regex-based analysis (fallback or additional)
if (this.config.fallbackToRegex || this.config.regexBasedOnly) {
try {
if (process.env.SUNLINT_DEBUG) {
console.log(`🔧 [S044] Trying regex-based analysis...`);
}
const regexViolations = await this.regexAnalyzer.analyze(filePath);
// Add to violation map with deduplication
regexViolations.forEach((violation) => {
const key = `${violation.line}:${violation.column}:${violation.message}`;
if (!violationMap.has(key)) {
violationMap.set(key, violation);
}
});
if (process.env.SUNLINT_DEBUG) {
console.log(
`🔧 [S044] Regex analysis completed: ${regexViolations.length} violations`
);
}
} catch (error) {
console.warn(`⚠ [S044] Regex analysis failed:`, error.message);
}
}
// Convert Map values to array and add filePath to each violation
const finalViolations = Array.from(violationMap.values()).map(
(violation) => ({
...violation,
filePath: filePath,
file: filePath, // Also add 'file' for compatibility
})
);
if (process.env.SUNLINT_DEBUG) {
console.log(
`🔧 [S044] File analysis completed: ${finalViolations.length} unique violations`
);
}
return finalViolations;
}
/**
* Clean up resources
*/
cleanup() {
if (this.symbolAnalyzer?.cleanup) {
this.symbolAnalyzer.cleanup();
}
if (this.regexAnalyzer?.cleanup) {
this.regexAnalyzer.cleanup();
}
}
}
module.exports = S044Analyzer;