@sun-asterisk/sunlint
Version:
☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards
318 lines (296 loc) • 13.4 kB
JavaScript
/**
* Rule Mapping Service
* Following Rule C005: Single responsibility - handle rule mapping logic
* Following Rule C015: Use domain language - clear naming for rule mappings
*/
class RuleMappingService {
constructor() {
this.eslintToSunlintMapping = this.createEslintToSunlintMapping();
this.sunlintToEslintMapping = this.createSunlintToEslintMapping();
}
/**
* Create ESLint to SunLint rule mapping
* Following Rule C012: Query - pure function
*/
createEslintToSunlintMapping() {
return {
// Custom rules mapping (using actual rule IDs from integrations/eslint/plugin/rules)
'custom/c002': 'C002',
'custom/c003': 'C003',
'custom/c006': 'C006',
'custom/c010': 'C010',
'custom/c013': 'C013',
'custom/c014': 'C014',
'custom/c017': 'C017',
'custom/c018': 'C018',
'custom/c023': 'C023',
'custom/c027': 'C027',
'custom/c029': 'C029',
'custom/c030': 'C030',
'custom/c034': 'C034',
'custom/c035': 'C035',
'custom/c041': 'C041',
'custom/c042': 'C042',
'custom/c043': 'C043',
'custom/c047': 'C047',
'custom/c048': 'C048',
'custom/c076': 'C076',
'custom/t002': 'T002',
'custom/t003': 'T003',
'custom/t004': 'T004',
'custom/t007': 'T007',
'custom/t011': 'T011',
'custom/t019': 'T019',
'custom/t025': 'T025',
'custom/t026': 'T026',
// Additional mappings for semantic rule names
'custom/no-console-error': 'C019',
// Security rules mapping
'custom/typescript_s001': 'S001',
'custom/typescript_s002': 'S002',
'custom/typescript_s003': 'S003',
'custom/typescript_s005': 'S005',
'custom/typescript_s006': 'S006',
'custom/typescript_s007': 'S007',
'custom/typescript_s008': 'S008',
'custom/typescript_s009': 'S009',
'custom/typescript_s010': 'S010',
'custom/typescript_s011': 'S011',
'custom/typescript_s012': 'S012',
'custom/typescript_s013': 'S013',
'custom/typescript_s014': 'S014',
'custom/typescript_s015': 'S015',
'custom/typescript_s016': 'S016',
'custom/typescript_s017': 'S017',
'custom/typescript_s018': 'S018',
'custom/typescript_s019': 'S019',
'custom/typescript_s020': 'S020',
'custom/typescript_s022': 'S022',
'custom/typescript_s023': 'S023',
'custom/typescript_s025': 'S025',
'custom/typescript_s026': 'S026',
'custom/typescript_s027': 'S027',
'custom/typescript_s029': 'S029',
'custom/typescript_s030': 'S030',
'custom/typescript_s033': 'S033',
'custom/typescript_s034': 'S034',
'custom/typescript_s035': 'S035',
'custom/typescript_s036': 'S036',
'custom/typescript_s037': 'S037',
'custom/typescript_s038': 'S038',
'custom/typescript_s039': 'S039',
'custom/typescript_s041': 'S041',
'custom/typescript_s042': 'S042',
'custom/typescript_s043': 'S043',
'custom/typescript_s044': 'S044',
'custom/typescript_s045': 'S045',
'custom/typescript_s046': 'S046',
'custom/typescript_s047': 'S047',
'custom/typescript_s048': 'S048',
'custom/typescript_s050': 'S050',
'custom/typescript_s052': 'S052',
'custom/typescript_s054': 'S054',
'custom/typescript_s055': 'S055',
'custom/typescript_s057': 'S057',
'custom/typescript_s058': 'S058',
'custom/single-responsibility': 'C005',
'custom/verb-noun-naming': 'C006',
'custom/no-direct-new': 'C014',
'custom/domain-language': 'C015',
'custom/separate-validation': 'C031',
'custom/no-constructor-api': 'C032',
'custom/separate-logic-data': 'C033',
'custom/no-global-state': 'C034',
'custom/full-error-logging': 'C035',
'custom/standard-response': 'C037',
'custom/no-order-dependency': 'C038',
'custom/centralized-validation': 'C040',
// Standard ESLint rules that map to SunLint rules
'prefer-const': 'C_PREFER_CONST',
'no-unused-vars': 'C_NO_UNUSED_VARS',
'no-console': 'C019', // Maps to logging rule
'no-undef': 'C_NO_UNDEF', // Undefined variables
'consistent-return': 'C_CONSISTENT_RETURN',
'no-var': 'C_NO_VAR',
'eqeqeq': 'C_STRICT_EQUALITY',
'no-eval': 'S_NO_EVAL',
'no-implied-eval': 'S_NO_IMPLIED_EVAL',
'no-new-func': 'S_NO_NEW_FUNC',
'max-lines-per-function': 'C_MAX_LINES_PER_FUNCTION',
'complexity': 'C_COMPLEXITY',
'no-new': 'C_NO_NEW',
'curly': 'C_CURLY_BRACES',
};
}
/**
* Create SunLint to ESLint rule mapping
* Following Rule C012: Query - pure function
*/
createSunlintToEslintMapping() {
const mapping = {};
// Reverse the ESLint to SunLint mapping
for (const [eslintRule, sunlintRule] of Object.entries(this.eslintToSunlintMapping)) {
if (!mapping[sunlintRule]) {
mapping[sunlintRule] = [];
}
mapping[sunlintRule].push(eslintRule);
}
// Add additional direct mappings
mapping['C005'] = ['custom/single-responsibility']; // C005 doesn't have direct ESLint rule, fallback to basic
mapping['C006'] = ['custom/c006']; // Verb-noun naming
mapping['C014'] = ['custom/c014']; // Dependency injection
mapping['C015'] = ['custom/domain-language']; // Domain language - no direct ESLint rule
mapping['C019'] = ['custom/c043', 'no-console']; // Console/logging
mapping['C031'] = ['custom/separate-validation']; // Validation - no direct ESLint rule
mapping['C032'] = ['custom/no-constructor-api']; // Constructor API - no direct ESLint rule
mapping['C033'] = ['custom/separate-logic-data']; // Logic separation - no direct ESLint rule
mapping['C034'] = ['custom/c034']; // Implicit return
mapping['C035'] = ['custom/c035']; // Empty catch
mapping['C037'] = ['custom/standard-response']; // Standard response - no direct ESLint rule
mapping['C038'] = ['custom/no-order-dependency']; // Order dependency - no direct ESLint rule
mapping['C040'] = ['custom/centralized-validation']; // Centralized validation - no direct ESLint rule
// Additional mappings for available custom rules
mapping['C002'] = ['custom/c002']; // Duplicate code
mapping['C003'] = ['custom/c003']; // Vague abbreviations
mapping['C010'] = ['custom/c010']; // Block nesting
mapping['C013'] = ['custom/c013']; // Dead code
mapping['C017'] = ['custom/c017']; // Constructor logic
mapping['C018'] = ['custom/c018']; // Generic throw
mapping['C023'] = ['custom/c023']; // Duplicate variable name
mapping['C027'] = ['custom/c027']; // Function nesting
mapping['C029'] = ['custom/c029']; // Catch block logging
mapping['C030'] = ['custom/c030']; // Custom error classes
mapping['C041'] = ['custom/c041']; // Config inline
mapping['C042'] = ['custom/c042']; // Boolean naming
mapping['C043'] = ['custom/c043']; // Console/print
mapping['C047'] = ['custom/c047']; // Duplicate retry logic
mapping['C048'] = ['custom/c048']; // Var declaration
mapping['C076'] = ['custom/c076']; // One assert per test
// Security rules mapping
mapping['S001'] = ['custom/typescript_s001']; // Fail securely
mapping['S002'] = ['custom/typescript_s002']; // IDOR check
mapping['S005'] = ['custom/typescript_s005']; // No Origin header auth
mapping['S006'] = ['custom/typescript_s006']; // Activation recovery secret
mapping['S007'] = ['custom/typescript_s007']; // No plaintext OTP
mapping['S008'] = ['custom/typescript_s008']; // Crypto agility
mapping['S009'] = ['custom/typescript_s009']; // No insecure crypto
mapping['S010'] = ['custom/typescript_s010']; // No insecure random
mapping['S011'] = ['custom/typescript_s011']; // No insecure UUID
mapping['S012'] = ['custom/typescript_s012']; // No hardcoded secrets
mapping['S013'] = ['custom/typescript_s013']; // Verify TLS connection
mapping['S014'] = ['custom/typescript_s014']; // Insecure TLS version
mapping['S015'] = ['custom/typescript_s015']; // Insecure TLS certificate
mapping['S016'] = ['custom/typescript_s016']; // Sensitive query parameter
mapping['S017'] = ['custom/typescript_s017']; // No SQL injection
mapping['S018'] = ['custom/typescript_s018']; // Positive input validation
mapping['S019'] = ['custom/typescript_s019']; // No raw user input in email
mapping['S020'] = ['custom/typescript_s020']; // No eval dynamic execution
mapping['S022'] = ['custom/typescript_s022']; // Output encoding required
mapping['S023'] = ['custom/typescript_s023']; // No JSON injection
mapping['S025'] = ['custom/typescript_s025']; // Server side input validation
mapping['S026'] = ['custom/typescript_s026']; // JSON schema validation
mapping['S027'] = ['custom/typescript_s027']; // No hardcoded secrets advanced
mapping['S029'] = ['custom/typescript_s029']; // Require CSRF protection
mapping['S030'] = ['custom/typescript_s030']; // No directory browsing
mapping['S033'] = ['custom/typescript_s033']; // Require SameSite cookie
mapping['S034'] = ['custom/typescript_s034']; // Require Host cookie prefix
mapping['S035'] = ['custom/typescript_s035']; // Cookie specific path
mapping['S036'] = ['custom/typescript_s036']; // No unsafe file include
mapping['S037'] = ['custom/typescript_s037']; // Require anti cache headers
mapping['S038'] = ['custom/typescript_s038']; // No version disclosure
mapping['S039'] = ['custom/typescript_s039']; // No session token in URL
mapping['S041'] = ['custom/typescript_s041']; // Require session invalidate on logout
mapping['S042'] = ['custom/typescript_s042']; // Require periodic reauthentication
mapping['S043'] = ['custom/typescript_s043']; // Terminate sessions on password change
mapping['S044'] = ['custom/typescript_s044']; // Require full session for sensitive ops
mapping['S045'] = ['custom/typescript_s045']; // Anti automation controls
mapping['S046'] = ['custom/typescript_s046']; // Secure notification on auth change
mapping['S048'] = ['custom/typescript_s048']; // Password credential recovery
mapping['S050'] = ['custom/typescript_s050']; // Session token weak hash
mapping['S052'] = ['custom/typescript_s052']; // Secure random authentication code
mapping['S054'] = ['custom/typescript_s054']; // Verification default account
mapping['S057'] = ['custom/typescript_s057']; // UTC logging
mapping['S058'] = ['custom/typescript_s058']; // No SSRF
return mapping;
}
/**
* Get ESLint rules for SunLint rule ID
* Following Rule C006: Verb-noun naming
*/
getEslintRulesForSunLintRule(sunlintRuleId) {
return this.sunlintToEslintMapping[sunlintRuleId] || [];
}
/**
* Get SunLint rule for ESLint rule ID
* Following Rule C006: Verb-noun naming
*/
getSunLintRuleForEslintRule(eslintRuleId) {
return this.eslintToSunlintMapping[eslintRuleId] || eslintRuleId;
}
/**
* Get all supported SunLint rules
* Following Rule C006: Verb-noun naming
*/
getSupportedSunLintRules() {
return Object.keys(this.sunlintToEslintMapping);
}
/**
* Get all supported ESLint rules
* Following Rule C006: Verb-noun naming
*/
getSupportedEslintRules() {
return Object.keys(this.eslintToSunlintMapping);
}
/**
* Check if SunLint rule is supported by ESLint
* Following Rule C006: Verb-noun naming
*/
isSunLintRuleSupportedByEslint(sunlintRuleId) {
return this.sunlintToEslintMapping.hasOwnProperty(sunlintRuleId);
}
/**
* Get rule description for SunLint rule
* Following Rule C015: Use domain language
*/
getSunLintRuleDescription(sunlintRuleId) {
const descriptions = {
'C005': 'Each function should do only one thing',
'C006': 'Function names should be verb-noun',
'C014': 'Use Dependency Injection instead of direct new',
'C015': 'Use domain language in class/function names',
'C019': 'Do not use error level logging for non-critical errors',
'C031': 'Data validation logic should be separate',
'C032': 'No external API calls in constructor or static block',
'C033': 'Separate processing logic and data query in service layer',
'C034': 'Limit direct access to global state in domain logic',
'C035': 'When handling errors, log full relevant information',
'C037': 'API handlers should return standard response objects',
'C038': 'Avoid logic dependent on file/module loading order',
'C040': 'Do not scatter validation logic across multiple classes',
};
return descriptions[sunlintRuleId] || 'No description available';
}
/**
* Get rule category for SunLint rule
* Following Rule C015: Use domain language
*/
getSunLintRuleCategory(sunlintRuleId) {
const categories = {
'C005': 'quality',
'C006': 'naming',
'C014': 'architecture',
'C015': 'naming',
'C019': 'logging',
'C031': 'validation',
'C032': 'architecture',
'C033': 'architecture',
'C034': 'architecture',
'C035': 'logging',
'C037': 'api',
'C038': 'architecture',
'C040': 'validation',
};
return categories[sunlintRuleId] || 'other';
}
}
module.exports = RuleMappingService;