Specification: Custom Rules — Creating user-defined rules
Purpose
Enable customers to create, test, and deploy their own detection rules without any knowledge of the internal format, regex syntax, or analysis engine architecture.
1. Overview
| Tool |
Command |
Role |
| Wizard |
--create-rule |
Guided step-by-step creation, no technical knowledge required |
| Quick test |
--test-rule <id> |
Instant feedback on a single rule, without running a full audit |
| Examples |
--init-rule --examples |
Learning by example (commented .sca files) |
| Tool |
Command |
Role |
| Skeleton |
--init-rule <id> |
Generates an empty pre-filled .sca file |
| Validation |
--rules-validate |
Validates all custom rules (syntax, regex, fields) |
| Listing |
--rules-list |
Displays every loaded rule (builtin + custom) |
2. Interactive wizard (--create-rule)
2.1 User workflow
$ ./staticcodeaudit /my/project --create-rule
StaticCodeAudit — Rule creation
? Rule name (snake_case): detect_console_error
? Target language:
> python
javascript
java
csharp
php
html
yaml
? What to look for in the code?
Enter the text or pattern to detect.
Examples: "TODO", "console.error", "SELECT.*FROM.*WHERE"
> console.error(
? Search scope:
> Each line independently (line)
4-line window (context-4)
6-line window (context-6)
? Severity:
> LOW — Best practice, suggested improvement
MEDIUM — Issue to fix within a reasonable timeframe
HIGH — Critical issue, must be fixed immediately
? Must the file contain a specific pattern?
(Prerequisite condition — leave empty to skip)
>
? Must the file NOT contain?
(Exclusion — leave empty to skip)
>
? Short description (English): Residual console.error call
? Short description (French): Appel console.error residuel
? Risk (English): Error logging in production exposes internal state.
? Solution (English): Remove console.error or use a proper logging framework.
Rule preview:
rule detect_console_error
language javascript
severity LOW
match
pattern console\.error\(
scope line
end
message
en "Residual console.error call"
fr "Appel console.error residuel"
end
end
? Create this rule? (Y/n): Y
Rule created: audit-rules/detect_console_error.sca
Validated: 1 pattern, scope line, LOW
Next steps:
1. Test it: ./staticcodeaudit /my/project --test-rule detect_console_error
2. Run a full audit to include this rule in the report
2.2 Wizard behavior
Automatic regex escaping
The customer types raw text. The wizard automatically escapes regex special characters:
| Customer input |
Generated regex |
Explanation |
console.error( |
console\.error\( |
Dot and parenthesis escaped |
System.out.println |
System\.out\.println |
Dots escaped |
SELECT.*FROM |
SELECT.*FROM |
.* recognized as intentional |
\bfoo\b |
\bfoo\b |
Regex construct preserved |
Escaping rules:
- Characters
(, ), [, ], {, }, ., +, ?, ^, $, |, \ are escaped automatically
- EXCEPT when the customer uses explicit regex constructs:
.*, .+, \s, \w, \d, \b, [...], (a|b)
- The wizard detects these constructs and preserves them
- When in doubt, the wizard asks: "This looks like a regex. Use it as-is? (Y/n)"
Real-time validation
Each answer is validated immediately:
- Name:
[a-z][a-z0-9_]* — otherwise an error message appears and the prompt is repeated
- Pattern: compiled as regex — on error, the error message is shown and the prompt is repeated
- Severity: choice among the 3 values — no free-form input
Advanced mode (optional)
If the customer types --create-rule --advanced, additional questions appear:
- Confidence (0-100, default 80)
- Pattern-not (exclusion pattern — "Do NOT detect if this line contains...")
- Codebase scope (for requires — check across all files, not file by file)
- CWE/OWASP metadata (compliance)
2.3 Error handling
| Situation |
Behavior |
| Name already in use |
"This name already exists. Choose a different name." |
| Invalid regex |
"Invalid pattern: [error]. Fix it or type plain text." |
| Ctrl+C |
"Cancelled. No file was created." |
| Missing audit-rules/ directory |
Created automatically |
3. Quick test (--test-rule <id>)
3.1 User workflow
$ ./staticcodeaudit /my/project --test-rule detect_console_error
Testing rule 'detect_console_error' against 42 files...
src/components/App.jsx:18 — console.error("Failed to load", err);
src/utils/api.js:67 — console.error(response.statusText);
src/utils/api.js:102 — console.error("Network error:", e);
3 findings detected in 2 files (0.2s)
3.2 Behavior
- Loads ONLY the requested rule (no builtin rules, no other custom rules)
- Scans files for the matching language under
paths.include
- Displays each finding: file, line, code
- No HTML report, no baseline, no JSON export
- Times the execution
- Exit code: 0 if at least 1 finding, 1 if 0 findings
3.3 Options
| Option |
Behavior |
--test-rule <id> |
Tests the rule located in audit-rules/ |
--test-rule <id> --file src/app.py |
Tests against a single file |
--test-rule <id> --verbose |
Also displays scanned files with no match |
3.4 Error cases
| Situation |
Behavior |
| Rule not found |
"Rule 'xxx' not found in audit-rules/. Available rules: ..." |
| Invalid rule |
Displays validation errors (same as --rules-validate) |
| 0 files for the language |
"No .py file found in the configured paths." |
4.1 Installation
$ ./staticcodeaudit /my/project --init-rule --examples
5 examples installed in audit-rules/_examples/
01_simple_pattern.sca — Detection of a simple keyword
02_multiline_context.sca — Pattern over multiple lines
03_file_condition.sca — match + requires (combo)
04_exclusion_pattern.sca — Detect unless safe
05_requires_only.sca — File condition only
Run: ./staticcodeaudit /my/project --rules-validate
to verify the examples are valid.
4.2 Example contents
01_simple_pattern.sca
Detects console.log() in JavaScript.
The most basic format: a single-line pattern.
02_multiline_context.sca
Detects .execute(f"...") in Python over 4 lines (scope context-4).
The "with" keyword adds an AND condition on the same window.
03_file_condition.sca
Combines match + requires: detects Flask() only in
files that do not import CSRFProtect.
The finding points to the exact line of Flask(), not line 1.
04_exclusion_pattern.sca
Detects hashlib.md5() UNLESS "usedforsecurity=False" is present.
The "pattern-not" keyword excludes lines containing the safe pattern.
05_requires_only.sca
A standalone requires block (without match) produces a file-level finding (line 1).
Detects Django files with MIDDLEWARE but without HSTS.
5. Technical architecture
5.1 Files to create/modify
| File |
Action |
Description |
sca/rules/examples/ |
Create |
5 commented example .sca files |
locales/script/{en,fr,es,de}.json |
Modify |
Wizard and tester messages |
tests/test_rule_wizard.py |
Create |
Wizard tests (escaping, generation, validation) |
tests/test_rule_tester.py |
Create |
Tester tests (execution, output, edge cases) |
5.2 Dependencies
- Zero external dependency: the wizard uses
input() and print() (stdlib)
- No curses, no rich, no prompt_toolkit
- Compatible with every terminal (no mandatory ANSI sequences)
5.3 Regex escaping — algorithm
smart_escape(user_input):
1. If it contains explicit regex constructs (\s, \w, .*, .+, [...], (a|b))
→ return as-is (the user knows regex)
2. Otherwise → escape every special character with re.escape()
5.4 Integration with the existing engine
--test-rule reuses the existing executors (sca/executors/):
1. Loads the rule via load_rules_from_directory()
2. Compiles the regexes via _compile_rule_patterns()
3. Iterates files via _get_files_for_language() (reused from runner)
4. Executes via regex.execute() / file_check.execute() (reuses the executors)
5. Displays findings without generating a report
6. Tier-based gating (license)
| Tier |
Custom rules |
Features |
| Demo |
0 |
No custom rules |
| Solo |
20 regex |
--create-rule, --test-rule, --rules-validate |
| Team |
100 regex + 30 taint |
Same as Solo + advanced examples |
| Enterprise |
Unlimited |
Everything + encrypted rules |
Gating is already implemented in rule_engine._apply_license_gating().
7. Verification and tests
Unit tests (tests/test_rule_wizard.py)
- Regex escaping: plain text, intentional regex, edge cases
- .sca generation: valid format, correct fields, file created
- Creation-time validation: invalid name, invalid pattern, file already exists
Unit tests (tests/test_rule_tester.py)
- Execution against a fixture: finding detected, correct file/line
- Rule not found: clear error message
- 0 files for the language: informative message
--file option: tests a single file
Functional tests (tests/test_functional.py)
- E2E pipeline: custom rule in
audit-rules/ → finding present in HTML and JSON reports
- Wizard: input simulation → valid .sca file generated
Manual verification
--create-rule → answer the questions → file created and valid
--test-rule <id> → findings displayed quickly
--init-rule --examples → examples installed and valid
- Full audit → custom rules present in the report