Analyse Multi-Couches
Sommaire
- Objectif
- Architecture actuelle (mono-couche)
- Architecture proposee (multi-couches)
- Couche 1 : Pattern matching
- Couche 2 : Taint analysis
- Couche 3 : Context analysis
- Fusionneur de preuves
- Chaine de preuves dans le rapport
- Impact sur les faux positifs
- Limites
Objectif
Passer d'une analyse mono-couche (un seul mode par regle) a une analyse multi-couches ou chaque fichier est examine par 3 strategies independantes. Les resultats sont croises pour produire des findings a haute confiance avec une chaine de preuves verifiable par un humain.
Architecture proposee (multi-couches)
Pour chaque fichier, 3 couches s'executent en parallele :
┌─────────────────┐
│ Fichier source │
└────────┬────────┘
│
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Couche 1 │ │ Couche 2 │ │ Couche 3 │
│ Pattern │ │ Taint │ │ Context │
│ matching │ │ analysis │ │ analysis │
└──────┬─────┘ └──────┬─────┘ └──────┬─────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────┐
│ Fusionneur de preuves │
│ │
│ Croise les resultats des 3 couches. │
│ Calcule la confidence reelle. │
│ Construit la chaine de preuves. │
└────────────────┬────────────────────────┘
│
▼
┌───────────────┐
│ Finding │
│ + preuves │
│ + confidence │
└───────────────┘
Couche 1 : Pattern matching (existant, ameliore)
Ce qu'elle fait
Cherche des patterns syntaxiques dans le code (texte ou regex). C'est la couche actuelle — rapide, large couverture.
Ce qu'elle produit
{
"layer": "pattern",
"rule_key": "sql_injection_fstring",
"file": "src/db.py",
"line": 42,
"code": "cursor.execute(f\"SELECT * FROM users WHERE id = {user_id}\")",
"evidence": "Pattern '.execute(f\"' matche a la ligne 42"
}
Confiance seule : 65%
Un pattern match seul est un signal, pas une preuve. Le pattern peut matcher du code safe (ex: un test, un commentaire, une string de documentation).
Couche 2 : Taint analysis (existant, enrichi)
Ce qu'elle fait
Trace le flux de donnees depuis une source non fiable (input HTTP, CLI, fichier) jusqu'a un sink dangereux (execute, open, system).
Ce qu'elle produit
{
"layer": "taint",
"rule_key": "taint_sqli",
"file": "src/db.py",
"flow": {
"source": {
"line": 12,
"code": "user_id = request.args.get('id')",
"kind": "http"
},
"propagation": [
{"line": 15, "code": "user_id = int(user_id) if safe else user_id"},
{"line": 38, "code": "query = f\"SELECT * FROM users WHERE id = {user_id}\""}
],
"sink": {
"line": 42,
"code": "cursor.execute(query)",
"type": "sql_execute"
},
"sanitizers_found": false
},
"evidence": "Donnee HTTP (request.args) propage sans sanitization jusqu'a cursor.execute()"
}
Confiance seule : 85%
Un taint flow est une preuve forte — il montre le chemin complet. Mais il peut etre un faux positif si le sanitizer est dans une fonction appelee indirectement (hors du fichier).
Couche 3 : Context analysis (nouveau)
Ce qu'elle fait
Analyse le contexte du fichier pour renforcer ou infirmer les findings des couches 1 et 2. Repond a des questions :
-
Le fichier utilise-t-il un ORM (SQLAlchemy, Django ORM) ? Si oui, une injection SQL est moins probable.
-
Le fichier importe-t-il un framework de sanitization ?
- Le fichier est-il un test, un mock, un fixture ? Si oui, ignorer.
- Le fichier gere-t-il des requetes HTTP (routes, handlers) ?
- Le fichier contient-il des validations d'input (schemas, validators) ?
Ce qu'elle produit
{
"layer": "context",
"file": "src/db.py",
"signals": {
"is_test_file": false,
"has_orm": false,
"has_raw_sql": true,
"has_http_handler": true,
"has_input_validation": false,
"has_sanitizer": false,
"framework": "flask"
},
"evidence": "Fichier Flask avec SQL brut, pas d'ORM, pas de validation d'input"
}
Signaux detectes
La couche contexte combine des heuristiques file-level :
- Detection de fichier de test/mock/fixture (chemin contient
test_,_test.,mock,fixture,spec.). - Presence d'un ORM (SQLAlchemy, Django ORM, Tortoise, Peewee, Prisma...).
- Presence de SQL brut (
SELECT,INSERT,UPDATE,DELETE). - Presence d'un handler HTTP (Flask
@app.route, FastAPI@router.*, async(request)...). - Presence d'une validation d'input (Pydantic, Marshmallow, Cerberus, WTForms, Django forms...).
- Presence d'un sanitizer (Bleach,
html.escape, MarkupSafe, parameterize,quote()...).
Fusionneur de preuves
Calcul de la confidence
La confidence finale n'est pas une moyenne — c'est un calcul base sur les preuves independantes :
Confidence = base + bonus_taint + bonus_context - penalites
Cas concrets :
Pattern seul, pas de contexte :
65% (signal faible, peut etre un faux positif)
Pattern + contexte confirme (raw SQL, pas d'ORM, handler HTTP) :
65% + 15% = 80%
Pattern + taint flow (source → sink confirme) :
65% + 25% = 90%
Pattern + taint + contexte (triple confirmation) :
65% + 25% + 10% = 95% (quasi-certain)
Pattern + contexte INFIRME (ORM present, fichier de test) :
65% - 30% = 35% (probablement faux positif → supprime du rapport)
Regles de fusion
1. Si la couche contexte detecte un fichier de test → SUPPRIMER le finding
(les fichiers de test contiennent du code vulnerable volontairement)
2. Si la couche taint confirme le flux → AUGMENTER la confidence
et AJOUTER la chaine de preuves au finding
3. Si la couche contexte infirme (ORM present, sanitizer trouve) →
DIMINUER la confidence. Si < 40% → SUPPRIMER le finding
4. Si deux regles differentes detectent la meme vuln au meme endroit →
FUSIONNER en un seul finding avec la confidence la plus haute
5. Si le taint flow traverse plusieurs fichiers → AJOUTER le label
"cross-file" et AUGMENTER la confidence de 5%
Chaine de preuves dans le rapport
Ce que le developpeur voit aujourd'hui
[HIGH] SQL Injection (f-string) — src/db.py:42
Risk: An attacker can inject malicious SQL...
Solution: Use parameterized queries.
Pas de contexte. Le dev doit comprendre seul pourquoi c'est un probleme.
Ce que le developpeur verrait avec l'analyse multi-couches
[HIGH] SQL Injection (f-string) — src/db.py:42
Confidence: 95% (pattern + taint + context)
Preuves :
1. Source (ligne 12) :
user_id = request.args.get('id')
→ Donnee HTTP non fiable (kind: http)
2. Propagation (ligne 38) :
query = f"SELECT * FROM users WHERE id = {user_id}"
→ Variable injectee dans une requete SQL via f-string
3. Sink (ligne 42) :
cursor.execute(query)
→ Execution de la requete sans parametrisation
4. Contexte :
- Fichier Flask avec handler HTTP (@app.route)
- SQL brut (pas d'ORM)
- Pas de validation d'input detectee
- Pas de sanitizer detecte
Solution: Use parameterized queries.
Playbook: 3 etapes (diagnostic → correction → verification)
References: CWE-89, OWASP A03:2021, ISO A.8.28
Le developpeur peut verifier chaque etape de la preuve. S'il pense que c'est un faux positif, il sait exactement quoi verifier.
Impact sur les faux positifs
La couche contexte reduit drastiquement les faux positifs :
| Situation | Aujourd'hui | Multi-couches |
|---|---|---|
| Pattern dans un fichier de test | Finding (FP) | Supprime (is_test_file) |
| Pattern dans un fichier avec ORM | Finding (FP) | Confidence 35% → supprime |
| Pattern + taint confirme | Finding (80%) | Finding (95%) |
| Pattern dans un commentaire/string | Finding (FP) | Confidence 35% → supprime |
Estimation sur le benchmark actuel
| Metrique | Actuel | Multi-couches (estime) |
|---|---|---|
| Precision | 81% | 93%+ (contexte filtre les FP) |
| Rappel | 99% | 99% (meme couverture) |
| Score F1 | 89% | 96%+ |
Limites
L'analyse multi-couches ameliore la qualite des detections existantes. Elle ne peut pas detecter :
-
Les bugs logiques purs (ex: IDOR Lunary CVE-2024-1625 ou le probleme est un WHERE manquant dans une requete — pas un pattern syntaxique)
-
Les vulnerabilites dans les dependances (couvert par pip-audit, pas par SAST)
- Les vulnerabilites dans du code compile/obfusque
Pour ces cas, les solutions sont :
- IDOR : regle specifique "CRUD sans ownership check" (regle 4 du BountyBench)
- Dependances : scan pip-audit/npm audit (deja implemente)
- Code compile : hors scope SAST