Adding Custom Rules
Relevant source files
The following files were used as context for generating this wiki page:
Custom rules allow developers to extend the Unified Security Scanner's detection capabilities beyond the default rule set. By defining project-specific security patterns, teams can catch vulnerabilities unique to their architecture, enforce specific coding standards, and reduce false positives through fine-tuned matching logic.
Rules are primarily implemented using Semgrep YAML syntax, which enables powerful pattern matching across various languages including JavaScript, TypeScript, and JSON. These rules are integrated into the scanner's multi-phase orchestration, allowing them to be executed alongside traditional SAST tools and AI-enhanced validation layers.
Sources: README.md:144-156, CONTRIBUTING.md:31-35, QUICK_START.md:358-382
Rule Architecture and Storage
Custom rules are stored within the rules/ directory of the project. The scanner organizes rules into subdirectories based on the technology stack or tool they target, such as rules/react/ for frontend-specific logic or rules/npm/ for supply chain security.
Rule Directory Structure
rules/
├── react/ # React-specific security patterns
│ └── hooks.yml # Rules for useEffect, Context API, etc.
├── npm/ # Package manager security
│ └── security.yml # Rules for package.json scripts
└── custom/ # User-defined project-specific rules
Sources: CONTRIBUTING.md:31-35, QUICK_START.md:364-367, README.md:465-470
The following diagram illustrates how custom rules are ingested into the scanning pipeline:
The diagram shows the flow of custom rules from definition files through the multi-phase scanning engine to the final report.
Sources: README.md:20-25, CONTRIBUTING.md:15-23
Rule Definition Syntax
Rules must follow the Semgrep YAML format. Each rule file contains a rules list, where each entry defines a unique detection pattern, its severity, and associated metadata.
Core Rule Components
| Component | Description | Example |
|---|---|---|
id | Unique identifier for the rule | react-hardcoded-api-key |
patterns | Logic for matching code structures | pattern: const $KEY = "$VALUE" |
message | Human-readable explanation of the finding | "Hardcoded credential detected" |
severity | Impact level: ERROR, WARNING, or INFO | ERROR |
languages | Supported file types | [typescript, javascript] |
metadata | Contextual info like CWE or OWASP category | cwe: CWE-798 |
Sources: rules/react/hooks.yml:2-19, rules/npm/security.yml:2-17
Pattern Matching Logic
Rules use metavariables (e.g., $VAR, $VALUE) to capture code elements and metavariable-pattern with regex to validate those elements. For example, a rule detecting hardcoded secrets might look for strings over a certain length that match a specific variable naming convention.
- id: react-hardcoded-api-key-in-hook
patterns:
- pattern: |
const $HOOK = () => {
...
const $KEY = "$VALUE";
...
}
- metavariable-pattern:
metavariable: $KEY
patterns:
- pattern-regex: .*(API_KEY|SECRET|TOKEN|PASSWORD).*
Sources: rules/react/hooks.yml:2-14
Integrating Rules into Configuration
To enable custom rules, they must be referenced in the .securityrc.json configuration file. The scanner allows users to specify directories or individual files to be included in the scan.
Configuration Example
{
"tools": {
"semgrep": {
"enabled": true,
"rules": ["./rules/react/*.yml", "./rules/custom/*.yml"]
}
},
"customRules": {
"baseSecurityPolicy": "./rules/base-security-policy.txt",
"languageSpecific": {
"javascript": "./rules/js-security-rules.yml"
}
}
}
Sources: QUICK_START.md:363-368, README.md:144-156, configs/default.json:8-11
Testing and Validating Rules
Before deploying new rules, they should be validated against both vulnerable and safe code samples to ensure accuracy and maintain a low false-positive rate (target is <5%).
Validation Workflow
- Create Test Fixtures: Place sample code in a
tests/fixtures/directory. - Run Local Scan: Use the CLI to target the fixtures with the new rule.
- Verify Findings: Ensure the rule triggers on intended vulnerabilities and ignores safe patterns like sanitized input.
# Test a specific rule against a fixture
node packages/cli/dist/index.js scan ./tests/fixtures --config custom-rule-config.json
Sources: CONTRIBUTING.md:41-50, QUICK_START.md:148-160, README.md:41-43
Handling False Positives
If a rule is too permissive, it may flag legitimate code. The scanner provides a falsePositives configuration section to suppress known safe patterns using regex or file exclusions.
| Strategy | Implementation | Source |
|---|---|---|
| File Exclusion | Use exclude in scan config | configs/default.json:17-23 |
| Pattern Suppression | Define patterns in falsePositives | QUICK_START.md:374-382 |
| Sanitization Check | Use pattern-not for safe functions (e.g., DOMPurify.sanitize) | rules/react/hooks.yml:41-45 |
Best Practices for Custom Rules
To ensure high-performance and reliable scanning, follow these guidelines:
- Be Specific: Avoid overly broad regex patterns that scan the entire codebase unnecessarily.
- Include Remediation: Use the
messagefield to provide clear steps for fixing the identified issue. - Leverage CWE Mapping: Assign correct Common Weakness Enumeration (CWE) IDs in metadata to facilitate standardized reporting.
- Use Context-Aware Filtering: Ensure rules for secrets or sensitive data ignore test files by setting
excludeTestFiles: truein the configuration.
Sources: rules/react/hooks.yml:17-19, QUICK_START.md:372-373, SECURITY_REVIEW.md:204-215
In conclusion, adding custom rules is a structured process involving YAML definition, configuration integration, and rigorous testing. This extensibility ensures the Unified Security Scanner remains effective against evolving threats and specific project requirements.