
Un système d'audit de sécurité contextuel pour les artefacts de recherche
SAFE effectue une évaluation de sécurité contrôlée et consciente du dépôt des résultats Semgrep et Trivy dans les artefacts de recherche.
Il prend en charge deux tâches de classification indépendantes : la prédiction binaire directe (SECURITY_RELEVANT ou NON_SECURITY) et la taxonomie contextuelle détaillée multiclasse (trois étiquettes — voir Trois étiquettes). Chaque tâche peut s'exécuter en mode zero-shot ou agentique.
Il attend uniquement :
artifact_id.artifact_id.Il ne s'entraîne pas sur des données d'évaluation étiquetées et ne les ajuste pas. Les données étiquetées ne sont utilisées qu'après l'inférence, pour évaluer les prédictions, et ne sont jamais vues par le classifieur. SAFE n'exécute jamais le code des artefacts ; le texte du dépôt est traité comme une preuve non fiable, et non comme des instructions.
Cette version contient le code source complet de safe_audit, l'interface CLI et les tests, ainsi qu'un autonome de trois artefacts d'exemple entièrement synthétiques que vous pouvez exécuter de bout en bout sans aucune donnée externe. Elle exclut le corpus réel d'artefacts de recherche, les étiquettes de vérité terrain et les résultats d'évaluation utilisés dans l'article.
Démarrage rapide : après l'Installation, exécutez la Démo — elle fonctionne immédiatement sans configuration de données. config.example.yaml, couvert plus loin dans Configuration, est un modèle pour vos propres résultats/artefacts et ne s'exécutera pas tant que vous ne l'aurez pas modifié.
cd path/to/safe-artifact-auditor
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
Définissez la clé API :
export OPENAI_API_KEY="your-key"
Pour un proxy LiteLLM d'organisation, utilisez config.litellm.example.yaml à la place — il est commenté en ligne. Les identifiants et les valeurs d'en-tête personnalisées sont lus à partir des variables d'environnement et ne sont jamais stockés dans la configuration SAFE ou les fichiers de résultats.
demo/ contient trois petits artefacts d'exemple entièrement synthétiques — aucun dérivé ou correspondant à un quelconque artefact de recherche publié réel — un par étiquette de taxonomie, afin que les évaluateurs puissent exercer le pipeline complet sans aucune donnée externe :
demo-contextual-risk/ — un agrégateur de points de contrôle d'apprentissage fédéré jouet qui désérialise un point de contrôle téléchargé depuis une URL fournie par l'appelant avec torch.load. Une entrée non fiable provenant du réseau atteint un puits de désérialisation non sécurisé, que SAFE est censé classer CONTEXTUAL_RISK.demo-hardening-recommendation/ — un harnais de benchmark jouet qui exécute subprocess.run(..., shell=True) sur des lignes de commande qui sont toutes des littéraux Python codés en dur, sans aucune entrée contrôlée par l'appelant. SAFE est censé classer ceci HARDENING_RECOMMENDATION : le modèle shell est réel et mérite d'être signalé, mais rien d'externe ne peut l'atteindre ou l'influencer.demo-false-positive/ — un générateur de fixtures de test épinglé à une version plus ancienne de Pillow avec un avis hypothétique de bombe de décompression. Le code crée uniquement de nouvelles images en mémoire et n'ouvre jamais de données externes, donc le chemin de code réel de l'avis n'est jamais atteint. SAFE est censé classer ceci FALSE_POSITIVE.demo/findings.csv contient un résultat par artefact, et demo/demo-zero-shot.yaml / demo/demo-agentic.yaml sont des configurations prêtes à l'emploi (artifact_root: . se résout relativement au fichier de configuration, donc exécutez depuis l'intérieur de demo/) :
cd demo
safe-audit run --config demo-zero-shot.yaml
safe-audit run --config demo-agentic.yaml
Les résultats atterrissent respectivement dans demo/runs/demo-zero-shot/ et demo/runs/demo-agentic/ (voir Sortie).
CONTEXTUAL_RISKHARDENING_RECOMMENDATIONFALSE_POSITIVEAucune catégorie supplémentaire et aucune règle déterministe de changement d'étiquette n'est utilisée. Un mécanisme de recherche/sécurité documenté et isolé dans le code propre d'un artefact est classé HARDENING_RECOMMENDATION, car la pratique sous-jacente reste réelle même lorsque l'isolement limite l'exploitabilité réaliste.
SECURITY_RELEVANT : un risque contextuel valide ou une préoccupation de durcissement, y compris un comportement de recherche en sécurité intentionnel et isolé.NON_SECURITY : un résultat faux, non concordant, non applicable, absent ou manifestement inutilisé concernant une fonctionnalité affectée.L'évaluateur dérive également une vue binaire des prédictions multiclasses : FALSE_POSITIVE devient NON_SECURITY ; chaque autre étiquette multiclasse devient SECURITY_RELEVANT. Les résultats binaires directs et dérivés restent explicitement séparés.
project/
├── config.yaml
├── data/
│ └── findings.csv
└── artifacts/
├── artifact_001/
├── artifact_002/
└── artifact_003/
Le mappage est exact : artifact_id = artifact_001 se résout en artifacts/artifact_001/.
Colonnes CSV requises :
artifact_id;tool;finding_id
Colonnes facultatives :
artifact_id;tool;finding_id;category;severity_raw;file;line;message;package;version;cwe;cvss;scanner_applicable
Une colonne d'index initiale sans nom est ignorée. Les colonnes supplémentaires sont préservées par le modèle d'entrée.
Exemple :
artifact_id;tool;finding_id;category;severity_raw;file;line;message;package;version;cwe;cvss;scanner_applicable
artifact_001;semgrep;python.lang.security.audit.subprocess-shell-true;code;HIGH;src/probe.py;42;Shell command uses shell=True;;;;CWE-78;;yes
artifact_002;trivy;DEMO-CVE-0001;dependency;HIGH;;;Affected package (illustrative, not a real CVE);example-lib;1.2.0;CWE-502;8.1;yes
scripts/run_scanners.py et scripts/build_findings_csv.py produisent le findings.csv et la disposition des artefacts décrits ci-dessus directement à partir de votre propre code, en utilisant Semgrep et Trivy.
Installez Semgrep (fonctionne de la même manière sur tout système d'exploitation, y compris Linux) :
pip install semgrep
Installez Trivy sur Linux — soit le dépôt apt (Debian/Ubuntu) :
sudo apt-get install wget gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy
ou le script d'installation officiel, qui fonctionne sur toute distribution Linux et installe une version binaire dans /usr/local/bin (aucun paquet root requis au-delà de sudo pour ce répertoire) :
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin
Vérifiez que les deux sont sur PATH avant de continuer :
semgrep --version
trivy --version
Ensuite, disposez un répertoire par artefact sous un artifact_root/ et exécutez :
python scripts/run_scanners.py artifact_root --output scan-output
python scripts/build_findings_csv.py scan-output --output data/findings.csv
La première commande exécute Semgrep et Trivy (analyse des vulnérabilités et des secrets) contre chaque répertoire d'artefact et enregistre le JSON brut des scanners. La seconde analyse ce JSON dans un findings.csv compatible SAFE (les colonnes correspondent à la Structure d'entrée ; file est rapporté relativement à chaque répertoire d'artefact). Passez --skip-semgrep/--skip-trivy à l'un ou l'autre script pour exécuter un seul outil. --config sur run_scanners.py épingle un ensemble de règles Semgrep spécifique au lieu du auto par défaut, ce qui est pratique mais non reproductiblement épinglé.
Cette section concerne l'exécution de SAFE contre votre propre CSV de résultats et vos dossiers d'artefacts (voir Structure d'entrée ci-dessus). Si vous voulez simplement voir SAFE s'exécuter, utilisez la Démo à la place — config.example.yaml ci-dessous est un modèle et ne s'exécutera pas tel quel.
Copiez config.example.yaml :
cp config.example.yaml config.yaml
Ensuite, modifiez input_csv et artifact_root (et éventuellement paper_root) pour pointer vers vos propres données avant d'exécuter.
Paramètres clés :
model / provider : identifiant exact du modèle OpenAI (ou alias LiteLLM), et openai ou litellm avec l'URL du proxy et le nom de la variable d'environnement des identifiants.analysis_mode : zero_shot ou agentic.classification_task : binary ou multiclass ; indépendant de analysis_mode.max_agent_steps : requis uniquement dans une configuration agentique.max_workers / max_output_tokens / max_schema_retries : concurrence, plafond de sortie par réponse et budget de nouvelles tentatives d'appel de modèle pour les réponses invalides au schéma.resume / resume_policy : incomplete réessaie les échecs, les artefacts manquants et les résultats non tentés ; failed_only réessaie uniquement les échecs tout en conservant les succès enregistrés.cost : comptabilité des coûts en direct facultative et terminaison max_run_cost_usd.Le modèle par défaut est gpt-5.6-sol. Changez-le explicitement si la disponibilité, le coût ou les exigences de latence diffèrent.
safe-audit run --config config.yaml
Ou sans installer la commande console :
PYTHONPATH=src python -m safe_audit.cli run --config config.yaml
Pour une comparaison appariée exécutable contre les données synthétiques incluses, voir Démo (demo/demo-zero-shot.yaml et demo/demo-agentic.yaml). Elles ne diffèrent que par analysis_mode et run_name. Le mode zero-shot effectue un appel de modèle sur les preuves de base. Le mode agentique part des mêmes preuves et peut appeler des outils de dépôt en lecture seule limités avant de renvoyer le même résultat structuré.
runs/<run_name>/
├── config.resolved.yaml
├── run_metadata.json
├── summary.json
├── results.jsonl
├── results.csv
├── profiles/
├── evidence/
├── raw/<finding_uid>/
│ ├── 0001-request.json
│ ├── 0001-response.json (ou 0001-error.json)
│ └── final-output.txt
└── logs/
├── events.jsonl
├── result_attempts.jsonl
└── run_sessions.jsonl
results.csv est destiné à l'analyse. results.jsonl préserve les enregistrements structurés complets. Les preuves et les sorties brutes du modèle prennent en charge l'audit et l'analyse des erreurs. Les deux sont canoniques : ils ne contiennent que le dernier enregistrement pour chaque résultat, tandis que logs/result_attempts.jsonl est en ajout seul et préserve chaque résultat historique.
Lors de la reprise, SAFE ré-analyse d'abord les réponses brutes enregistrées de chaque résultat en échec avec l'analyseur strict actuel ; une classification valide unique est récupérée sans appel API. Seuls les échecs irrécupérables sont planifiés pour l'inférence du modèle. Pour une continuation en échec uniquement d'une exécution partiellement terminée, conservez le même output_root et run_name et définissez :
resume: true
resume_policy: failed_only
Pour évaluer les prédictions contre un CSV doré étiqueté (avec une colonne security_label ou security_class) :
safe-audit evaluate --results runs/<run_name>/results.jsonl --gold GOLD.csv --output runs/<run_name>/evaluation.json
PYTHONPATH=src python -m unittest discover -s tests -v
La suite de tests utilise un fournisseur factice et ne nécessite donc pas de clé API.