StaticCodeAudit
by CodeFixture
Standalone compliance, security and code quality audit tool for web projects (Python, JavaScript/TypeScript, HTML, Java, C#, PHP, YAML).
Table of Contents
- Installation
- Quick Start
- CLI Options
- Project Management
- Configuration
- Audit Categories
- Reports
- CI/CD Integration
- Internationalization (i18n)
- Custom Rules
Installation
- Download the binary archive for your platform from your customer area (
staticcodeaudit-linux-x64.tar.gz,staticcodeaudit-macos-arm64.tar.gz,staticcodeaudit-macos-x64.tar.gzorstaticcodeaudit-windows-x64.zip). - Extract the archive.
- Move the resulting directory anywhere on disk. The binary is fully self-contained — there is nothing to install on the target machine.
# Linux / macOS
tar -xzf staticcodeaudit-linux-x64.tar.gz
cd staticcodeaudit-linux-x64
# Windows (PowerShell)
Expand-Archive staticcodeaudit-windows-x64.zip
cd staticcodeaudit-windows-x64
In the rest of this document, the binary is referred to as staticcodeaudit-<platform> (replace <platform> with linux-x64, macos-arm64, macos-x64 or windows-x64).
Quick Start
# First run: automatically generates audit.config.json and registers the project
./staticcodeaudit-<platform> /path/to/my-project --init
# Run the audit
./staticcodeaudit-<platform> /path/to/my-project
The --init flag interactively creates audit.config.json by detecting:
- Project languages (Python, JavaScript/TypeScript, HTML, Java, C#, PHP)
- Source code directories (
app/,src/, etc.)
Important: At runtime, the script uses only the
audit.config.jsonconfiguration file — no auto-detection.
CLI Options
| Option | Description |
|---|---|
project_path |
Path to the project to audit (default: .) |
--init |
Create audit.config.json interactively |
--quick, -q |
Quick mode (security only) |
--fail-on-high |
Exit code 1 if HIGH vulnerabilities found (CI/CD) |
--sarif |
Generate SARIF 2.1.0 export (GitHub Code Scanning, GitLab SAST) |
--lang |
Report and console language (fr, en, es, de, default: en) |
--debug |
Debug logging (stderr + log file) |
--create-rule |
Create or edit a custom rule (unified interactive wizard) |
--init-rule |
Alias for --create-rule (backward compatibility) |
--custom-rules-match |
Check custom rules ↔ custom fixtures coverage |
--with-tests |
Auto-detect and run project unit tests |
--with-deps |
Run dependency vulnerability scan (pip-audit, npm audit) |
--rules-match |
Check that every builtin rule has matching fixtures |
--benchmark |
Run benchmark (precision, recall, F1 on fixtures) |
Examples:
# Full audit
./staticcodeaudit-linux-x64 /path/to/project
# Quick mode (security only)
./staticcodeaudit-linux-x64 /path/to/project --quick
# CI/CD mode (SARIF export + exit code 1 on HIGH)
./staticcodeaudit-linux-x64 /path/to/project --sarif --fail-on-high
./staticcodeaudit-linux-x64 /path/to/project --with-tests
# With dependency vulnerability scan
./staticcodeaudit-linux-x64 /path/to/project --with-deps
# French report
./staticcodeaudit-linux-x64 /path/to/project --lang=fr
# Create a custom rule (interactive taint wizard)
./staticcodeaudit-linux-x64 /path/to/project --init-rule
# Verify rules/fixtures coverage
./staticcodeaudit-linux-x64 --rules-match
# Benchmark (precision, recall, F1)
./staticcodeaudit-linux-x64 --benchmark
# Debug mode
./staticcodeaudit-linux-x64 /path/to/project --debug
Debug output goes to stderr and a log file ({output}/SCA-DEBUG-YYYY-MM-DD-HH-MM.log).
Each line includes: level, module, source file:line, function, thread and PID.
Debug messages are translated according to --script-lang (same as console messages).
# stderr (--script-lang=en, default)
[SCA-DBG] INFO sca.runner | runner.py:42 run() [MainThread:12345] | Config loaded: ...
# stderr (--script-lang=fr)
[SCA-DBG] INFO sca.runner | runner.py:42 run() [MainThread:12345] | Config chargée : ...
# log file (same format with timestamp)
2026-03-03 14:30:15 INFO sca.runner | runner.py:42 run() [MainThread:12345] | Config loaded: ...
# Project management
./staticcodeaudit-linux-x64 --list-projects # List all projects
./staticcodeaudit-linux-x64 /path/to/project --project-info # Project info
./staticcodeaudit-linux-x64 /path/to/project --init-fixtures # Create fixtures directory
./staticcodeaudit-linux-x64 /path/to/project --unregister # Unregister project
# List audit categories
./staticcodeaudit-linux-x64 --list-categories
./staticcodeaudit-linux-x64 --list-categories --script-lang=fr
# List all rules by category
./staticcodeaudit-linux-x64 --list-rules
./staticcodeaudit-linux-x64 /path/to/project --list-rules --script-lang=fr # with project fixtures
# Fixture validation
./staticcodeaudit-linux-x64 /path/to/project --self-test
Output Formats
Each audit run generates HTML + JSON by default. Additional formats are available on demand:
| Format | Content | Audience | How to enable |
|---|---|---|---|
| HTML | Full visual report with charts, sidebar, glossary | Humans (managers, developers) | Default |
| JSON | Raw audit data (findings, scores, metadata) | Automation, dashboards, CI/CD | Default |
| Demo HTML | Anonymized report (paths, code, solutions redacted) | Prospects, public demos | --demo |
| SARIF | Findings in OASIS standard format | IDEs, GitHub Code Scanning, GitLab SAST | --sarif |
| SBOM | CycloneDX 1.5 dependency inventory | Compliance, supply chain security | --sbom |
# Generate HTML + JSON + anonymized demo report
./staticcodeaudit-linux-x64 /path/to/project --demo
# Generate HTML + JSON + SARIF
./staticcodeaudit-linux-x64 /path/to/project --sarif
# Generate HTML + JSON + SBOM
./staticcodeaudit-linux-x64 /path/to/project --sbom
# Generate all formats
./staticcodeaudit-linux-x64 /path/to/project --sarif --sbom --demo
Demo mode: The
--demoflag generates an additional anonymized report (*-demo.html) alongside the full report. File paths are replaced withpath_to/{filename}:##, source code is hidden, and solutions are truncated. Ideal for sharing with prospects or publishing as a sample.
Project Management
Each audited project is identified by a unique UUID generated automatically during the first audit (--init). This system allows you to:
- Distinguish projects with the same directory name but at different locations
- Isolate project-specific fixtures
- Track audit history per project
- Detect if a project has been moved
First project audit
# The script automatically detects the project and generates a UUID
./staticcodeaudit-linux-x64 /path/to/my-project --init
# Output:
# 🔍 Auto-detecting project "my-project"...
# Type: Python + JavaScript
# Paths: app/, src/
# 🔑 Unique identifier generated: a1b2c3d4
# ✅ Configuration generated: /path/to/my-project/audit.config.json
# ✅ Project registered: projects/a1b2c3d4/
List registered projects
./staticcodeaudit-linux-x64 --list-projects
# Output:
# 📁 Registered projects (2):
#
# [a1b2c3d4] MyProject
# Path: /path/to/my-project
# Description: Example web application
# Last audit: 2026-02-25 10:30
# Audits: 15 | Fixtures: 12 specific
#
# [b5c6d7e8] My-API
# Path: /Users/dev/my-api
# Last audit: Never
# Audits: 0 | Fixtures: generic only
Detailed project information
./staticcodeaudit-linux-x64 /path/to/project --project-info
# Output:
# 📋 Project Information
#
# ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
# Name: MyProject
# Description: Example web application
# Version: 3.0
# Path: /path/to/my-project
#
# Registered: 2026-01-15 14:30
# Last audit: 2026-02-25 10:30
# Total audits: 15
#
# Fixtures:
# Specific: 12 (projects/a1b2c3d4/fixtures/)
# Generic: 42 (include_generic: true)
# Total: 54
Add project-specific fixtures
# Create the fixtures directory for a project
./staticcodeaudit-linux-x64 /path/to/project --init-fixtures
# Add files to:
# Audit/projects/{uuid}/fixtures/vulnerable/ # Files that should be detected
# Audit/projects/{uuid}/fixtures/clean/ # Files without vulnerabilities
Moved project detection
If a project is moved to a different location, the script automatically detects the change and updates the registered path:
⚠️ Warning: path has changed for this project
Previous: /Users/dev/old-path/my-project
New: /Users/dev/new-path/my-project
✅ project.json updated
Unregister a project
./staticcodeaudit-linux-x64 /path/to/project --unregister
# The project.json file is deleted
# Fixtures are preserved (if present)
# The project's audit.config.json is not modified
Configuration
The audit.config.json file is automatically generated at the target project root during the first audit. It is based on the templates/audit.config.template.json template.
brand
Branding configuration. Allows customizing the tool identity per project (e.g., for client-facing reports).
| Field | Type | Description |
|---|---|---|
tool_name |
string | Tool name displayed in the report (default: StaticCodeAudit) |
company_name |
string | Company name displayed in the report footer (default: CodeFixture) |
prefix |
string | Prefix for report and data files (default: SCA) |
logo |
string|null | Path to a client logo (SVG, PNG, JPG). Replaces favicon and header icon in the report. Relative to project root or absolute. |
{
"brand": {
"tool_name": "Acme Code Audit",
"company_name": "Acme Corp",
"prefix": "ACM",
"logo": "assets/acme-logo.svg"
}
}
Note: If
brand.logopoints to a missing file or an unsupported format, the tool falls back to its default icons with a console warning. The logo is embedded as base64 in the HTML report to keep it standalone.
project
Project information displayed in the report.
| Field | Type | Description |
|---|---|---|
id |
string | Unique UUID generated automatically at --init (do not modify) |
name |
string | Project name (auto-detected from directory name) |
version |
string | Project version (auto-detected from package.json or pyproject.toml) |
description |
string | Optional description |
{
"project": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "My Project",
"version": "1.0.0",
"description": "FastAPI + Vue.js web application"
}
}
Important: The UUID (
id) is generated automatically and should not be modified manually. It is used to uniquely identify the project and associate project-specific fixtures.
languages
REQUIRED — Declares the project languages. Controls which audit rules are executed.
| Value | Extensions scanned | Rules enabled |
|---|---|---|
python |
.py |
SQL Injection, secrets, debug mode, eval, deserialization, crypto, exceptions, etc. |
javascript |
.js, .jsx, .ts, .tsx, .mjs |
XSS, console.log, insecure RNG, client-side auth, DOM, inline style, etc. |
html |
.html, .htm, .xhtml, .shtml, .vue, .svelte, .ejs, .hbs, .njk, .jinja, .jinja2, .twig, .liquid, .mustache, .phtml, .erb, .jsp, .asp, .aspx, .cshtml |
Inline SVG, ARIA, buttons without label, etc. |
java |
.java |
SQL Injection, deserialization, XXE, crypto, CSRF, CORS, command injection, etc. |
csharp |
.cs |
SQL Injection, deserialization, XXE, crypto, LDAP, command injection, etc. |
php |
.php |
SQL Injection, command injection, file inclusion, XSS, deserialization, etc. |
yaml |
.yml, .yaml |
CI/CD security (GitHub Actions, GitLab CI) |
{
"languages": ["python", "javascript", "html", "java", "csharp", "php", "yaml"]
}
Important: If a language is not declared, the corresponding rules will not run. TypeScript (
.ts,.tsx) and JSX (.jsx,.mjs) files are treated as JavaScript — the same rules apply. No separate"typescript"language is needed.
paths
Scan path configuration.
| Field | Type | Description |
|---|---|---|
include |
array | REQUIRED - Directories to scan (relative to project root) |
exclude |
array | Patterns to ignore — supports 3 formats: glob (**/node_modules/**), directory name (alembic/), extension (*.min.js) |
Note:
paths.includeis used for all declared languages. File type filtering is done by extension. During--init, the configuration is auto-generated with detected paths.
{
"paths": {
"include": [
"app/",
"src/",
"UI-FRONT/js/",
"UI-ADMIN/js/"
],
"exclude": [
"**/node_modules/**",
"**/vendor/**",
"**/__pycache__/**",
"*.min.js",
"*.bundle.js"
]
}
}
reports
Report generation configuration.
| Field | Type | Description |
|---|---|---|
output_dir |
string | HTML report output directory |
history_dir |
string | JSON data storage directory (history) |
max_history |
integer | Maximum number of reports kept |
language |
string | HTML report language (fr, en, es, de, default: fr) |
{
"reports": {
"output_dir": "docs/audit-reports",
"history_dir": "docs/audit-reports/audit-datas",
"max_history": 10,
"language": "en"
}
}
categories
Audit category activation and weights.
| Field | Type | Description |
|---|---|---|
enabled |
boolean | Enable/disable the category |
weight |
integer | Category weight (1-5) — used for finding prioritization |
{
"categories": {
"security": { "enabled": true, "weight": 3 },
"architecture": { "enabled": true, "weight": 2 },
"ui": { "enabled": true, "weight": 1 },
"ux": { "enabled": true, "weight": 1 },
"maintenance": { "enabled": true, "weight": 1 }
}
}
Recommended weights:
security: 3 (critical)architecture: 2 (important)ui,ux,maintenance: 1 (standard)
rules
Detection rules configuration.
| Field | Type | Description |
|---|---|---|
disabled |
array | List of rules to ignore |
custom_patterns |
object | Custom paths for specific rules |
{
"rules": {
"disabled": [
"TODO/FIXME",
"console.log"
],
"custom_patterns": {
"admin_routes": "app/features/admin",
"services": "app/features",
"templates": "src/templates"
}
}
}
Available rules (324 rules):
Security:
SQL Injection: Non-parameterized SQL queries (f-string, concatenation)XSS:innerHTMLwithout sanitizationHardcoded Secrets: Hardcoded passwords and API keysDebug mode:debug=Truein productionHTTP without TLS: Insecure HTTP URLsEval/Exec: Dangerous use ofeval()/exec()Secret logged: Secrets exposed in logsDeserialization: Insecurepickle.loads(),yaml.load()Weak crypto:hashlib.md5,hashlib.sha1OS Injection:subprocesswithshell=TrueVerbose exception: Exposed stack tracesCatch-all:except:without specific typeInsecure RNG:Math.random()for sensitive valuesClient-side auth: Access control vialocalStorageHomebrew auth: Custom password comparison instead of proper hashingPredictable session: Weak RNG for tokens/sessionsDynamic import: Unrestricted dynamic module loadingRace condition: TOCTOU file access patternsJNDI Injection: JNDI lookup with user input (Log4Shell, Java)XPath Injection: XPath queries with string concatenation (Java, C#, PHP, Python)SSTI: Server-Side Template Injection (Jinja2, Pug, EJS, Nunjucks)CSV Injection: CSV output without formula character sanitizationSMTP Injection: Email headers with unsanitized user input (Python, PHP)ReDoS: Regular expressions with nested quantifiersFormat String: User input in format strings (.format(), String.format())Missing HSTS: HTTP framework without Strict-Transport-SecurityMissing X-Content-Type-Options: Response without nosniff headerMissing Referrer-Policy: Framework without Referrer-Policy headerMissing Frame Protection: Without frame-ancestors CSP or X-Frame-OptionsPostMessage No Origin Check: addEventListener("message") without origin validationMissing SRI: External scripts without Subresource IntegritySVG Scriptable Content: SVG elements with embedded scripts or event handlersGraphQL Introspection: Introspection enabled in productionGraphQL No Depth Limit: GraphQL without query depth/cost limiterWebSocket No TLS: ws:// connections instead of wss://File Upload No Validation: Upload without MIME type or extension checkWeak Password Policy: Minimum password length under 8 charactersDefault Credentials: Hardcoded admin/root/test passwordsSecurity Questions: Knowledge-based authentication patternsJWT None Algorithm: JWT accepting "none" algorithmJWT Hardcoded Secret: JWT signing key hardcoded in sourceInsufficient Key Size: RSA key under 2048 bitsLog Injection: Unsanitized user input in log messages (CRLF)
Architecture:
File too long: Files exceeding the line thresholdN+1 Query: Database queries inside a loop
UI:
Inline style: Direct.stylemanipulationcreateElement: Manual DOM element creationInline SVG: SVG directly in HTMLEvent listeners: Listeners without cleanupDOM in loop: DOM manipulation inside a loop
UX:
Untranslated toast: Hardcoded text in toastsEphemeral toast: Error toast without persistenceconsole.log: Residual debug logsARIA: Buttons withoutaria-label
Maintenance:
TODO/FIXME: TODO, FIXME, HACK, XXX commentsDeprecated API: Usage of deprecated stdlib functions
GDPR:
PII logged: Personal data in log statements (5 languages)Missing data retention: No data lifecycle management
Dockerfile:
Root user: Container running as rootUnpinned base: FROM without version tagCOPY all: COPY . . exposing sensitive files
Java (18 rules):
- SQL Injection, deserialization, XXE, command injection, Spring CSRF/CORS, weak crypto/RNG, SSL bypass, open redirect, SpEL injection, verbose exception, secret logged
C# (17 rules):
- SQL Injection, deserialization, XXE, command injection, weak crypto/RNG, SSL bypass, XSS raw HTML, CORS, open redirect, verbose exception, secret logged, LDAP injection, request validation disabled
PHP (22 rules):
- SQL Injection, command injection, eval, deserialization, file inclusion, XXE, XSS echo/Blade, weak crypto/RNG, open redirect, extract, type juggling, mass assignment, verbose exception, secret logged
CI/CD (18 rules):
- pull_request_target_checkout, GHA expression injection, excessive/missing permissions, unguarded comment trigger, unpinned action version, GitLab unsafe variables, workflow not in CODEOWNERS
- gha_secret_in_log, gha_deprecated_commands, gha_artifact_poisoning, gha_self_hosted_runner
- ci_curl_pipe_bash, ci_insecure_download, hardcoded_secret_cicd, docker_latest_tag
- gitlab_allow_failure_security, gitlab_script_secrets_echo
Frontend XSS (4 rules):
- React dangerous innerHTML setter, Angular DomSanitizer bypass, Vue v-html, Svelte @html
ORM/SQL Injection (12 rules):
- Django raw, SQLAlchemy text, JPA native, MyBatis, Dapper, PHP whereRaw/wpdb/Doctrine, Sequelize, Mongoose NoSQL, Prisma, TypeORM
MFA Detection (5 rules):
- Missing MFA in Python, Java, C#, JavaScript, PHP
SSRF (5 rules):
- Server-Side Request Forgery in Python (requests/urllib/httpx), Java (URL), C# (HttpClient), PHP (file_get_contents/curl), JavaScript (fetch/axios)
Path Traversal (4 rules):
- Path traversal in Python (open/Path), Java (File/Paths), C# (File.ReadAllText/Path.Combine), JavaScript (fs.readFile)
Framework Security (7 rules):
- Django: @csrf_exempt, DEBUG=True, hardcoded SECRET_KEY
- Flask: debug=True, hardcoded secret_key
- Express: missing Helmet, permissive CORS
Cookie & LDAP (3 rules):
- Insecure cookie (missing HttpOnly/Secure) in Python, Java, C#, JavaScript, PHP
- LDAP injection in Python, Java
ISO 27001 A.8 Coverage (6 rules):
- Insecure local storage (tokens/secrets in localStorage)
- Missing CSP header (Flask, Django, Express without Content-Security-Policy)
- Hardcoded internal IP (RFC 1918 private addresses)
- Exposed test endpoint (unprotected /test, /debug routes)
- Destructive without backup (DROP/TRUNCATE outside migrations)
- Local time usage (datetime.now() without timezone)
tests
Unit test configuration (optional).
| Field | Type | Description |
|---|---|---|
enabled |
boolean | Enable test execution |
tests_dir |
string | Tests directory |
command |
string | Execution command ({tests_dir} will be replaced) |
{
"tests": {
"enabled": true,
"tests_dir": "tests/",
}
}
fixtures
Fixture configuration for rule validation (--self-test mode).
| Field | Type | Description |
|---|---|---|
include_generic |
boolean | If true, also includes generic fixtures as fallback |
parallel |
boolean | Enable parallel fixture execution |
parallel_workers |
integer | Number of parallel workers |
fixture_timeout |
integer | Timeout per fixture (seconds) |
{
"fixtures": {
"include_generic": true,
"parallel": true,
"parallel_workers": 4,
"fixture_timeout": 30
}
}
Fixture organization:
Fixtures are organized by project UUID:
Audit/
├── projects/
│ └── {uuid}/ # Project directory (first 8 characters)
│ └── fixtures/ # Project-specific fixtures
│ ├── vulnerable/ # Files that should be detected
│ └── clean/ # Files without vulnerabilities
│
└── generic/ # Generic fixtures (fallback)
├── vulnerable/
└── clean/
Loading priority:
1. Project-specific fixtures (projects/{uuid}/fixtures/)
Create fixture directories for a project:
./staticcodeaudit-linux-x64 /path/to/project --init-fixtures
thresholds
Thresholds for health score and CI/CD integration.
| Field | Type | Description |
|---|---|---|
max_high |
integer | Maximum tolerated HIGH vulnerabilities (0 = none) |
max_medium |
integer | Maximum tolerated MEDIUM vulnerabilities |
min_health |
integer | Minimum required health score (0-100) |
health_good |
integer | Threshold for a "good" score (green) |
health_warning |
integer | Threshold for a "warning" score (orange) |
{
"thresholds": {
"max_high": 0,
"max_medium": 10,
"min_health": 80,
"health_good": 90,
"health_warning": 70
}
}
Exit codes:
0: Audit passed, thresholds met1: Thresholds exceeded (HIGH vulnerabilities or insufficient score)2: Fixture validation error (--self-testmode)
sla
SLA configuration by severity (optional).
| Field | Type | Description |
|---|---|---|
enabled |
boolean | Enable SLA section in report (default: false) |
rules |
object | SLA rules per severity level |
{
"sla": {
"enabled": true,
"rules": {
"CRITICAL": { "delay": "4h", "escalation": "CTO" },
"HIGH": { "delay": "24h", "escalation": "Tech Lead" },
"MEDIUM": { "delay": "1 sprint", "escalation": "Team Lead" },
"LOW": { "delay": "Backlog", "escalation": "Developer" }
}
}
}
retention
Report retention configuration (optional). Controls automatic cleanup of old reports.
| Field | Type | Description |
|---|---|---|
mode |
string|null | Cleanup mode: "count", "days", "both", or null (default: null = no cleanup) |
max_count |
integer | Maximum number of reports to keep (default: 10, used in count or both mode) |
max_days |
integer | Maximum age in days (default: 90, used in days or both mode) |
{
"retention": {
"mode": "both",
"max_count": 10,
"max_days": 90
}
}
Modes:
"count": Keep only the N most recent reports (max_count)"days": Delete reports older than N days (max_days)"both": Apply both rules (report must satisfy both to be kept)null: No automatic cleanup (default)
Safety: At least one report is always preserved. Reports are deleted in HTML+JSON pairs. Use --retention-dry-run to preview which files would be deleted without actually removing them.
Note: The existing
reports.max_historysetting controls chart display limits only — it does not delete files. Useretentionfor actual file cleanup.
Audit Categories
| Category | Description | Example Rules |
|---|---|---|
| SECURITY | Security vulnerabilities | XSS, SQL Injection, secrets, deserialization, weak crypto, RNG, client-side auth |
| ARCHITECTURE | Architecture violations | Files too long, N+1 queries |
| UI | Interface issues | Accessibility (ARIA), inline SVG, inline styles |
| UX | User experience | Untranslated toasts, non-persistent errors |
| MAINTENANCE | Code maintainability | TODO/FIXME, console.log, dead code, deprecated APIs |
| CICD | CI/CD pipeline security | 18 rules: GHA injection, unpinned actions, secrets in logs, curl|bash, hardcoded credentials, docker :latest, GitLab allow_failure on SAST |
| DEPENDENCIES | Dependency vulnerabilities | CVE scanning, unpinned versions, non-compliant licenses |
Reports
Reports are generated in <project>/docs/audit-reports/:
docs/audit-reports/
SCA-REPORT-2026-02-22-15-30.html # Interactive HTML report
audit-datas/
SCA-DATA-2026-02-22-15-30.json # JSON data (history)
Each report is a standalone HTML file — all CSS, JavaScript, charts, and favicons are embedded inline (no external dependencies). Open it in any browser, share it, or archive it.
The HTML report includes:
- Project information (name, version, description, UUID, path)
- Audit parameters: languages, scanned extensions per language, include/exclude paths, categories
- Global health score (logarithmic, security-weighted, LOC-normalized) with visual indicator
- Breakdown by severity (CRITICAL, HIGH, MEDIUM, LOW, INFO)
- Breakdown by category
- Detailed findings list with source code
- Git committer attribution per finding (when
--git-blameis enabled) - Comparison with previous audit (new/resolved)
- Heatmap of most problematic files
- Fixture validation with detailed results
CI/CD Integration
# GitHub Actions
- name: Run Audit
run: |
# GitLab CI
audit:
script:
allow_failure: false
The script returns a non-zero exit code if:
- HIGH vulnerabilities are detected (
--fail-on-highormax_high: 0) - The health score is below the threshold (
min_health)
Internationalization (i18n)
The audit tool supports four languages: French (fr), English (en), Spanish (es) and German (de).
Two independent configurations:
| Configuration | Description | Default |
|---|---|---|
| Script language | Console messages (Security Scan..., Report generated...) |
en |
| Report language | HTML content (titles, labels, captions) | en |
Script language (CLI)
The language of console messages and debug logs is configured via the --script-lang option:
# Messages in English (default)
./staticcodeaudit-linux-x64 /path/to/project
# Messages in French
./staticcodeaudit-linux-x64 /path/to/project --script-lang=fr
# Messages in Spanish
./staticcodeaudit-linux-x64 /path/to/project --script-lang=es
# Messages in German
./staticcodeaudit-linux-x64 /path/to/project --script-lang=de
Report language (audit.config.json)
The language of the generated HTML report is configured in the project's audit.config.json file:
{
"reports": {
"output_dir": "docs/audit-reports",
"history_dir": "docs/audit-reports/audit-datas",
"max_history": 10,
"language": "es"
}
}
Supported values: fr (French), en (English), es (Spanish), de (German)
The report displays a language badge in the header (e.g., 🇫🇷 Francais, 🇬🇧 English, 🇪🇸 Espanol, 🇩🇪 Deutsch).
Translation files
Translation files are stored in the locales/ directory of the audit tool:
Audit/
locales/
script/
fr.json # Console messages in French
en.json # Console messages in English
es.json # Console messages in Spanish
de.json # Console messages in German
report/
fr.json # HTML report in French
en.json # HTML report in English
es.json # HTML report in Spanish
de.json # HTML report in German
Fallback: If an unsupported language is specified, the script automatically falls back to English (en).
Combination examples
# Script EN + Report EN (default)
./staticcodeaudit-linux-x64 /path/to/project
# Script FR + Report EN
./staticcodeaudit-linux-x64 /path/to/project --script-lang=fr
# Script EN + Report FR (set "language": "fr" in audit.config.json)
./staticcodeaudit-linux-x64 /path/to/project
# Script FR + Report FR
./staticcodeaudit-linux-x64 /path/to/project --script-lang=fr
# (with audit.config.json containing "language": "fr")
Custom Rules
Two types of custom rules: builtin (contributed to SCA) and client (specific to a project).
Client Custom Rules — Wizard (recommended)
The fastest way to create a rule for your project:
./staticcodeaudit-linux-x64 . --create-rule # Create or edit a custom rule
./staticcodeaudit-linux-x64 . --custom-rules-match # Check coverage (rules ↔ fixtures)
The interactive wizard guides you through 7 steps:
1. Name, language, category, severity — with duplicate check (name + pattern)
2. Pattern to detect (text or regex) + what neutralizes it (optional)
3. Risk description — EN required, FR/ES/DE optional
4. Solution — EN required, FR/ES/DE optional
5. Benefit — optional
6. Code example before/after (fix_before + fix_after) — optional
7. Preview → validation → write to custom-rules/{lang}/{cat}/{rule_id}.sca
If a rule with the same name already exists, the wizard proposes to edit it (pre-filled with current values).
Builtin Rules — Manual
Adding a new builtin rule requires changes in 6 locations. Follow this guide step by step.
Overview
| Step | File(s) | Action |
|---|---|---|
| 1 | sca/rules/builtin/{lang}/{cat}/ |
Create .sca rule file |
| 2 | locales/report/{fr,en,es,de}.json |
Add rule translations (all 4 languages) |
Step 1: Detection Logic
Complete pattern:
# In _audit_security() (or other _audit_*() method)
py_files = self._find_files(self._py_exts, self._py_paths) # or self._js_exts, self._html_exts
for filepath in py_files:
for line_num, line in self._read_file(filepath):
if re.search(r'your_detection_pattern', line):
r = self._rule("your_rule_key")
self._add_finding(
"SECURITY", # category: SECURITY, ARCH, UI, UX, MAINTENANCE
r["name"], # localized rule name
filepath, # file path
line_num, # line number
line, # code snippet
"HIGH", # severity: CRITICAL, HIGH, MEDIUM, LOW, INFO
r["risk"], # localized risk description
r["solution"], # localized solution
r["benefit"], # localized benefit
confidence=90, # 0-100, how likely this is a real issue
rule_key="your_rule_key"
)
Note: When
--git-blameis enabled, eachFindingobject is enriched with acommitterfield (string) containing the name of the last person who modified the line, resolved viagit blame. This field isNoneby default.
File path helpers:
self._find_files(self._py_exts, self._py_paths)— Python files (.py)self._find_files(self._js_exts, self._js_paths)— JavaScript/TypeScript files (.js,.jsx,.ts,.tsx,.mjs)self._find_files(self._html_exts, self._html_paths)— HTML/template files (.html,.htm,.xhtml,.vue,.svelte,.ejs,.hbs,.njk,.jinja2,.twig,.liquid, etc.)self._find_files(self._java_exts, self._java_paths)— Java files (.java)self._find_files(self._csharp_exts, self._csharp_paths)— C# files (.cs)self._find_files(self._php_exts, self._php_paths)— PHP files (.php)self._find_files(self._yaml_exts, self._yaml_paths)— YAML files (.yml,.yaml)
Note:
_find_files()automatically excludes files matchingpaths.excludepatterns (glob, directory name, extension). No manual filtering needed.
Step 2: Translations (4 languages)
Add the rule key in all 4 files under "rules":
locales/report/fr.json:
"your_rule_key": {
"name": "Nom de la règle",
"risk": "Description du risque en français.",
"solution": "Comment corriger le problème.",
"benefit": "Bénéfice après correction (ex: CWE-XXX)."
}
locales/report/en.json:
"your_rule_key": {
"name": "Rule Name",
"risk": "Risk description in English.",
"solution": "How to fix the issue.",
"benefit": "Benefit after fix (e.g., CWE-XXX)."
}
Repeat for es.json (Spanish) and de.json (German) with the same structure.
Step 3: Fixtures
# VULNERABLE: Description of the vulnerability
# Expected: Should trigger your_rule_key detection (HIGH severity)
def vulnerable_function():
# Minimal code that triggers the detection
dangerous_call(user_input)
# CLEAN: Description of the safe pattern
# Expected: Should NOT trigger your_rule_key detection
def safe_function():
# Correct implementation
safe_call(sanitized_input)
Naming conventions:
- Vulnerable: use the rule key as filename (e.g.,
sql_injection_fstring.py) - Clean: use a descriptive safe name (e.g.,
sql_parameterized.py) - Extension matches the language (
.py,.js,.html)
Step 4: Register Fixtures
VULNERABLE_FIXTURES = {
# ...existing entries...
"your_rule_key": ("your_rule_key.py", "app/target_path.py", "Rule Name"),
}
CLEAN_FIXTURES = {
# ...existing entries...
"safe_alternative": ("safe_alternative.py", "app/target_path.py", "Rule Name"),
}
Tuple format: (fixture_filename, target_path_in_temp_project, rule_name_for_docs)
The target_path must match a directory in paths.include config (e.g., app/, UI-FRONT/).
Step 5: Tests
class TestYourRule:
"""Tests for your_rule_key detection."""
def test_detects_vulnerability(self, temp_project, audit_runner):
"""Must detect the vulnerable pattern."""
fixture = VULNERABLE_FIXTURES["your_rule_key"]
use_vulnerable_fixture(temp_project, fixture[0], fixture[1])
audit_runner._audit_security() # or _audit_architecture(), etc.
findings = get_findings_by_rule_key(audit_runner, "your_rule_key")
assert len(findings) >= 1
def test_ignores_safe_code(self, temp_project, audit_runner):
"""Must NOT detect the safe pattern."""
fixture = CLEAN_FIXTURES["safe_alternative"]
use_clean_fixture(temp_project, fixture[0], fixture[1])
audit_runner._audit_security()
findings = get_findings_by_rule_key(audit_runner, "your_rule_key")
assert len(findings) == 0
Run your tests: ```bash
Run only your new tests
Run all tests (check for regressions)
Checklist
Before submitting, verify:
- [ ] Rule defined in all 4 locale files (
fr.json,en.json,es.json,de.json) - [ ] Vulnerable fixture created and triggers detection
- [ ] Clean fixture created and does NOT trigger detection
- [ ] Test class with at least 2 tests (detect + ignore)
License
MIT