phi-complexity 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/PKG-INFO +14 -1
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/README.md +13 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/securite.py +182 -1
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/PKG-INFO +14 -1
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/pyproject.toml +1 -1
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_securite.py +149 -4
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/LICENSE +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/__init__.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/__main__.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/akasha.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/analyseur.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/audit_slo.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/autosuture.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/__init__.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/asm_light.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/base.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/c_rust_light.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/elf_light.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/python.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backup.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/bmad.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/canvas.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/cli.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/commit_risk.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/consensus.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/core.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/coverage_reporting.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/fingerprint.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/gnose.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/harvest.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/impossible.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/log_parser.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/metadata_ops.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/metriques.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/notebook_helpers.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/oracle.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/rapport.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/remediation.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/search.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/__init__.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/bayesian.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/behavior.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/host.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/response.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/telemetry.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/suture.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/vault.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/SOURCES.txt +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/dependency_links.txt +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/entry_points.txt +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/requires.txt +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/top_level.txt +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/setup.cfg +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_akasha.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_analyseur.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_asm_backend.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_audit_slo.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_autosuture.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_backends.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_backup.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_bmad.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_canvas.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_cli.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_commit_risk.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_consensus.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_core.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_coverage_reporting.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_elf_backend.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_engine.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_fingerprint.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_gnose.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_harvest.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_impossible.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_log_parser.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_main.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_metadata_ops.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_metriques.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_notebook_helpers.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_oracle.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_public_api_contract.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_rapport.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_remediation.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_scan_cli.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_search.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_sentinel.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_suture.py +0 -0
- {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_vault.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: phi-complexity
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Code quality metrics based on Golden Ratio (φ) mathematical invariants
|
|
5
5
|
Author-email: Tomy Verreault <Tiloup777@outlook.fr>
|
|
6
6
|
License: MIT
|
|
@@ -245,6 +245,19 @@ MIT — Tomy Verreault, 2026
|
|
|
245
245
|
|
|
246
246
|
*Anchored in the Bibliothèque Céleste — Morphic Phi Framework (φ-Meta)*
|
|
247
247
|
|
|
248
|
+
## 📜 Historique des Versions (Changelog)
|
|
249
|
+
|
|
250
|
+
- **v0.2.2 (Souveraineté & Cybersécurité) — Actuelle** :
|
|
251
|
+
- Implémentation du **"Cerveau de Sécurité"** : Moteur de triage mathématique départageant les failles critiques cybernétiques (CWE-79, CWE-89) des simples anomalies architecturales (Variance Lilith, Complexité).
|
|
252
|
+
- Algorithmes de **Mémoire Fail-Fast** pour `phi_check` optimisant à 100% le temps processeur et la RAM lors des audits anti-mots-interdits.
|
|
253
|
+
- Conformité CI parfaite garantissant aucune friction entre Windows et Linux (via normalisation PEP8 stricte).
|
|
254
|
+
|
|
255
|
+
- **v0.2.1** : Intégration souveraine du `phi_check` pré-commit natif et de la politique `Quality Gate` pour GitHub Actions. Réparation des métriques de base.
|
|
256
|
+
- **v0.2.0** : Introduction de pydantic pour la vérification de la robustesse, ajouts de structures initiales pour les métriques `Souverainete`.
|
|
257
|
+
- **v0.1.0** : Version primaire. Formule mathématique de base implémentée (Lilith, Shannon, Fib, Radiance).
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
248
261
|
## 🔒 Sécurité : Activation du contrôle phi-check
|
|
249
262
|
|
|
250
263
|
Ce projet utilise un contrôle automatique (pré-commit) qui bloque tout ajout de code contenant certains mots ou outils réservés (par exemple des scripts ou outils reconnus de piratage).
|
|
@@ -213,6 +213,19 @@ MIT — Tomy Verreault, 2026
|
|
|
213
213
|
|
|
214
214
|
*Anchored in the Bibliothèque Céleste — Morphic Phi Framework (φ-Meta)*
|
|
215
215
|
|
|
216
|
+
## 📜 Historique des Versions (Changelog)
|
|
217
|
+
|
|
218
|
+
- **v0.2.2 (Souveraineté & Cybersécurité) — Actuelle** :
|
|
219
|
+
- Implémentation du **"Cerveau de Sécurité"** : Moteur de triage mathématique départageant les failles critiques cybernétiques (CWE-79, CWE-89) des simples anomalies architecturales (Variance Lilith, Complexité).
|
|
220
|
+
- Algorithmes de **Mémoire Fail-Fast** pour `phi_check` optimisant à 100% le temps processeur et la RAM lors des audits anti-mots-interdits.
|
|
221
|
+
- Conformité CI parfaite garantissant aucune friction entre Windows et Linux (via normalisation PEP8 stricte).
|
|
222
|
+
|
|
223
|
+
- **v0.2.1** : Intégration souveraine du `phi_check` pré-commit natif et de la politique `Quality Gate` pour GitHub Actions. Réparation des métriques de base.
|
|
224
|
+
- **v0.2.0** : Introduction de pydantic pour la vérification de la robustesse, ajouts de structures initiales pour les métriques `Souverainete`.
|
|
225
|
+
- **v0.1.0** : Version primaire. Formule mathématique de base implémentée (Lilith, Shannon, Fib, Radiance).
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
216
229
|
## 🔒 Sécurité : Activation du contrôle phi-check
|
|
217
230
|
|
|
218
231
|
Ce projet utilise un contrôle automatique (pré-commit) qui bloque tout ajout de code contenant certains mots ou outils réservés (par exemple des scripts ou outils reconnus de piratage).
|
|
@@ -16,6 +16,7 @@ import hashlib
|
|
|
16
16
|
import hmac
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
|
+
import re
|
|
19
20
|
import secrets
|
|
20
21
|
import sys
|
|
21
22
|
import time
|
|
@@ -617,6 +618,173 @@ def _regle_phi_est_securite(categorie: str) -> bool:
|
|
|
617
618
|
return categorie.upper().startswith("CWE-")
|
|
618
619
|
|
|
619
620
|
|
|
621
|
+
_CWE_TAXONOMY: Dict[str, Dict[str, str]] = {
|
|
622
|
+
"79": {
|
|
623
|
+
"category": "injection",
|
|
624
|
+
"label": "Cross-Site Scripting (XSS)",
|
|
625
|
+
"vector": "web",
|
|
626
|
+
"playbook": "Échapper le contenu selon le contexte et appliquer une CSP.",
|
|
627
|
+
},
|
|
628
|
+
"89": {
|
|
629
|
+
"category": "injection",
|
|
630
|
+
"label": "SQL Injection",
|
|
631
|
+
"vector": "database",
|
|
632
|
+
"playbook": "Utiliser des requêtes paramétrées et valider les entrées.",
|
|
633
|
+
},
|
|
634
|
+
"134": {
|
|
635
|
+
"category": "memory",
|
|
636
|
+
"label": "Format String",
|
|
637
|
+
"vector": "native",
|
|
638
|
+
"playbook": "Utiliser des formats sûrs et éviter printf sur entrée utilisateur.",
|
|
639
|
+
},
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
_PHI_QUALITY_TAXONOMY: Dict[str, Dict[str, str]] = {
|
|
643
|
+
"CYCLOMATIQUE": {
|
|
644
|
+
"category": "complexity",
|
|
645
|
+
"label": "Complexité cyclomatique",
|
|
646
|
+
"playbook": "Découper les fonctions et réduire les branches.",
|
|
647
|
+
},
|
|
648
|
+
"NESTING": {
|
|
649
|
+
"category": "complexity",
|
|
650
|
+
"label": "Niveau d'imbrication élevé",
|
|
651
|
+
"playbook": "Extraire les blocs imbriqués en fonctions dédiées.",
|
|
652
|
+
},
|
|
653
|
+
"LILITH": {
|
|
654
|
+
"category": "quality",
|
|
655
|
+
"label": "Variance LILITH",
|
|
656
|
+
"playbook": "Stabiliser la structure et réduire l'entropie du code.",
|
|
657
|
+
},
|
|
658
|
+
"FIBONACCI": {
|
|
659
|
+
"category": "quality",
|
|
660
|
+
"label": "Courbes Fibonacci",
|
|
661
|
+
"playbook": "Lisser la progression des structures et éliminer les pics.",
|
|
662
|
+
},
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def _extraire_cwe(rule_id: str) -> Optional[str]:
|
|
667
|
+
match = re.search(r"(?i)\bcwe[-_ ]?(\d{1,5})\b", rule_id.strip())
|
|
668
|
+
return match.group(1) if match else None
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
def _priority_par_severite(severite: str, surface: str, security_relevant: bool) -> str:
|
|
672
|
+
niveau = severite.lower()
|
|
673
|
+
if not security_relevant:
|
|
674
|
+
return "P4"
|
|
675
|
+
if surface == "production" and niveau in {"critical", "high"}:
|
|
676
|
+
return "P0"
|
|
677
|
+
if niveau in {"critical", "high"}:
|
|
678
|
+
return "P1"
|
|
679
|
+
if niveau == "medium":
|
|
680
|
+
return "P2"
|
|
681
|
+
return "P3"
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def classer_finding(
|
|
685
|
+
finding: Dict[str, Any], memo: Optional[Dict[str, Dict[str, Any]]] = None
|
|
686
|
+
) -> Dict[str, Any]:
|
|
687
|
+
"""Classe un finding en catégorie exploitable pour la triage sécurité."""
|
|
688
|
+
source = str(finding.get("source", "")).strip() or "unknown"
|
|
689
|
+
rule_id = str(finding.get("rule_id", "UNKNOWN")).strip() or "UNKNOWN"
|
|
690
|
+
severite = str(finding.get("severity", "medium"))
|
|
691
|
+
surface = str(finding.get("surface", "production"))
|
|
692
|
+
security_relevant = _est_finding_securite(finding)
|
|
693
|
+
cwe_id = _extraire_cwe(rule_id)
|
|
694
|
+
decision_basis = (
|
|
695
|
+
"explicit-security-relevant"
|
|
696
|
+
if "security_relevant" in finding
|
|
697
|
+
else "rule-based-fallback"
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
taxonomy = (
|
|
701
|
+
_CWE_TAXONOMY.get(cwe_id, {})
|
|
702
|
+
if cwe_id
|
|
703
|
+
else _PHI_QUALITY_TAXONOMY.get(rule_id.upper(), {})
|
|
704
|
+
)
|
|
705
|
+
family = "security" if security_relevant else "quality"
|
|
706
|
+
signature = f"{source}:{rule_id}"
|
|
707
|
+
reused = False
|
|
708
|
+
|
|
709
|
+
if memo is not None and signature in memo:
|
|
710
|
+
precedent = memo[signature]
|
|
711
|
+
if precedent.get("decision") in {"security", "quality"}:
|
|
712
|
+
family = str(precedent.get("decision"))
|
|
713
|
+
decision_basis = "registry-reuse"
|
|
714
|
+
reused = True
|
|
715
|
+
|
|
716
|
+
if taxonomy:
|
|
717
|
+
category = taxonomy.get("category", "unknown")
|
|
718
|
+
label = taxonomy.get("label", rule_id)
|
|
719
|
+
vector = taxonomy.get("vector", "generic")
|
|
720
|
+
playbook = taxonomy.get("playbook", "")
|
|
721
|
+
else:
|
|
722
|
+
category = family
|
|
723
|
+
label = rule_id
|
|
724
|
+
vector = "generic"
|
|
725
|
+
playbook = ""
|
|
726
|
+
|
|
727
|
+
priority = _priority_par_severite(severite, surface, family == "security")
|
|
728
|
+
|
|
729
|
+
learning = {
|
|
730
|
+
"signature": signature,
|
|
731
|
+
"decision": family,
|
|
732
|
+
"basis": decision_basis,
|
|
733
|
+
"reused": reused,
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
classification = {
|
|
737
|
+
"family": family,
|
|
738
|
+
"category": category,
|
|
739
|
+
"label": label,
|
|
740
|
+
"vector": vector,
|
|
741
|
+
"priority": priority,
|
|
742
|
+
"decision_basis": decision_basis,
|
|
743
|
+
"cwe": f"CWE-{cwe_id}" if cwe_id else None,
|
|
744
|
+
"playbook": playbook,
|
|
745
|
+
"learning": learning,
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if memo is not None:
|
|
749
|
+
memo[signature] = {"decision": family, "basis": decision_basis}
|
|
750
|
+
|
|
751
|
+
return classification
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
def classer_findings(
|
|
755
|
+
findings: List[Dict[str, Any]],
|
|
756
|
+
memo: Optional[Dict[str, Dict[str, Any]]] = None,
|
|
757
|
+
) -> Dict[str, Dict[str, Any]]:
|
|
758
|
+
registry: Dict[str, Dict[str, Any]] = memo or {}
|
|
759
|
+
for finding in findings:
|
|
760
|
+
finding["classification"] = classer_finding(finding, registry)
|
|
761
|
+
return registry
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def _resume_classification(findings: List[Dict[str, Any]]) -> Dict[str, Dict[str, int]]:
|
|
765
|
+
par_famille: Dict[str, int] = {"security": 0, "quality": 0, "unknown": 0}
|
|
766
|
+
famille_inattendue: Dict[str, int] = {}
|
|
767
|
+
par_categorie: Dict[str, int] = {}
|
|
768
|
+
par_priorite: Dict[str, int] = {}
|
|
769
|
+
for finding in findings:
|
|
770
|
+
classification = finding.get("classification", {})
|
|
771
|
+
famille = str(classification.get("family", "quality"))
|
|
772
|
+
if famille not in par_famille:
|
|
773
|
+
famille_inattendue[famille] = famille_inattendue.get(famille, 0) + 1
|
|
774
|
+
famille = "unknown"
|
|
775
|
+
categorie = str(classification.get("category", "unknown"))
|
|
776
|
+
priorite = str(classification.get("priority", "P4"))
|
|
777
|
+
par_famille[famille] += 1
|
|
778
|
+
par_categorie[categorie] = par_categorie.get(categorie, 0) + 1
|
|
779
|
+
par_priorite[priorite] = par_priorite.get(priorite, 0) + 1
|
|
780
|
+
return {
|
|
781
|
+
"by_family": par_famille,
|
|
782
|
+
"by_category": par_categorie,
|
|
783
|
+
"by_priority": par_priorite,
|
|
784
|
+
"unexpected_family": famille_inattendue,
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
|
|
620
788
|
def _est_finding_securite(finding: Dict[str, Any]) -> bool:
|
|
621
789
|
"""Détermine si un finding doit impacter le score/policy sécurité.
|
|
622
790
|
|
|
@@ -626,7 +794,17 @@ def _est_finding_securite(finding: Dict[str, Any]) -> bool:
|
|
|
626
794
|
- autres sources (SARIF/outils externes) : sécurité par défaut.
|
|
627
795
|
"""
|
|
628
796
|
if "security_relevant" in finding:
|
|
629
|
-
|
|
797
|
+
security_relevant = finding.get("security_relevant")
|
|
798
|
+
if isinstance(security_relevant, bool):
|
|
799
|
+
return security_relevant
|
|
800
|
+
if isinstance(security_relevant, str):
|
|
801
|
+
normalized = security_relevant.strip().lower()
|
|
802
|
+
if normalized in {"false", "0", "no", "off"}:
|
|
803
|
+
return False
|
|
804
|
+
if normalized in {"true", "1", "yes", "on"}:
|
|
805
|
+
return True
|
|
806
|
+
if isinstance(security_relevant, (int, float)):
|
|
807
|
+
return bool(security_relevant)
|
|
630
808
|
source = str(finding.get("source", "")).lower()
|
|
631
809
|
rule_id = str(finding.get("rule_id", ""))
|
|
632
810
|
if source == "phi-complexity":
|
|
@@ -833,6 +1011,8 @@ def construire_audit_securite(
|
|
|
833
1011
|
if not include_demo:
|
|
834
1012
|
findings = [f for f in findings if f.get("surface") != "demo"]
|
|
835
1013
|
|
|
1014
|
+
classer_findings(findings)
|
|
1015
|
+
classification_summary = _resume_classification(findings)
|
|
836
1016
|
score = _score_securite(findings)
|
|
837
1017
|
blocking = [
|
|
838
1018
|
f
|
|
@@ -853,6 +1033,7 @@ def construire_audit_securite(
|
|
|
853
1033
|
"blocking_findings": len(blocking),
|
|
854
1034
|
"out_of_scope_findings": len(out_of_scope),
|
|
855
1035
|
"status": "PASS" if len(blocking) == 0 else "FAIL",
|
|
1036
|
+
"classification": classification_summary,
|
|
856
1037
|
},
|
|
857
1038
|
"governance": {
|
|
858
1039
|
"severity_distribution": severites,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: phi-complexity
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Code quality metrics based on Golden Ratio (φ) mathematical invariants
|
|
5
5
|
Author-email: Tomy Verreault <Tiloup777@outlook.fr>
|
|
6
6
|
License: MIT
|
|
@@ -245,6 +245,19 @@ MIT — Tomy Verreault, 2026
|
|
|
245
245
|
|
|
246
246
|
*Anchored in the Bibliothèque Céleste — Morphic Phi Framework (φ-Meta)*
|
|
247
247
|
|
|
248
|
+
## 📜 Historique des Versions (Changelog)
|
|
249
|
+
|
|
250
|
+
- **v0.2.2 (Souveraineté & Cybersécurité) — Actuelle** :
|
|
251
|
+
- Implémentation du **"Cerveau de Sécurité"** : Moteur de triage mathématique départageant les failles critiques cybernétiques (CWE-79, CWE-89) des simples anomalies architecturales (Variance Lilith, Complexité).
|
|
252
|
+
- Algorithmes de **Mémoire Fail-Fast** pour `phi_check` optimisant à 100% le temps processeur et la RAM lors des audits anti-mots-interdits.
|
|
253
|
+
- Conformité CI parfaite garantissant aucune friction entre Windows et Linux (via normalisation PEP8 stricte).
|
|
254
|
+
|
|
255
|
+
- **v0.2.1** : Intégration souveraine du `phi_check` pré-commit natif et de la politique `Quality Gate` pour GitHub Actions. Réparation des métriques de base.
|
|
256
|
+
- **v0.2.0** : Introduction de pydantic pour la vérification de la robustesse, ajouts de structures initiales pour les métriques `Souverainete`.
|
|
257
|
+
- **v0.1.0** : Version primaire. Formule mathématique de base implémentée (Lilith, Shannon, Fib, Radiance).
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
248
261
|
## 🔒 Sécurité : Activation du contrôle phi-check
|
|
249
262
|
|
|
250
263
|
Ce projet utilise un contrôle automatique (pré-commit) qui bloque tout ajout de code contenant certains mots ou outils réservés (par exemple des scripts ou outils reconnus de piratage).
|
|
@@ -28,7 +28,10 @@ from phi_complexity.securite import (
|
|
|
28
28
|
evaluer_politique_gouvernance,
|
|
29
29
|
detecter_drift_heuristique,
|
|
30
30
|
construire_dossier_preuve,
|
|
31
|
+
_extraire_cwe,
|
|
31
32
|
_est_finding_securite,
|
|
33
|
+
classer_finding,
|
|
34
|
+
classer_findings,
|
|
32
35
|
)
|
|
33
36
|
|
|
34
37
|
CODE_C_VULNERABLE = """\
|
|
@@ -575,11 +578,28 @@ class TestAuditSecurite:
|
|
|
575
578
|
|
|
576
579
|
audit = construire_audit_securite([file_path])
|
|
577
580
|
summary = audit["summary"]
|
|
581
|
+
assert audit["findings"], "Audit should return at least one finding."
|
|
582
|
+
assert all(
|
|
583
|
+
"security_relevant" in f for f in audit["findings"]
|
|
584
|
+
), "Each finding should include security classification metadata."
|
|
585
|
+
# Verify all findings are quality annotations, not security rules
|
|
586
|
+
expected_quality_rule_ids = {
|
|
587
|
+
"CYCLOMATIQUE",
|
|
588
|
+
"LILITH",
|
|
589
|
+
"FIBONACCI",
|
|
590
|
+
"NESTING",
|
|
591
|
+
}
|
|
592
|
+
assert all(
|
|
593
|
+
f["rule_id"] in expected_quality_rule_ids for f in audit["findings"]
|
|
594
|
+
), "All findings should be quality annotations, got: " + str(
|
|
595
|
+
[f["rule_id"] for f in audit["findings"]]
|
|
596
|
+
)
|
|
578
597
|
non_security = [
|
|
579
598
|
f for f in audit["findings"] if not f.get("security_relevant", True)
|
|
580
599
|
]
|
|
581
600
|
|
|
582
601
|
assert non_security
|
|
602
|
+
assert len(non_security) == len(audit["findings"])
|
|
583
603
|
assert summary["blocking_findings"] == 0
|
|
584
604
|
assert summary["security_score"] == 100.0
|
|
585
605
|
assert summary["status"] == "PASS"
|
|
@@ -589,32 +609,157 @@ class TestAuditSecurite:
|
|
|
589
609
|
def test_est_finding_securite_prioritize_security_relevant(self):
|
|
590
610
|
assert _est_finding_securite({"security_relevant": True}) is True
|
|
591
611
|
assert _est_finding_securite({"security_relevant": False}) is False
|
|
612
|
+
assert _est_finding_securite({"security_relevant": "false"}) is False
|
|
613
|
+
assert _est_finding_securite({"security_relevant": "true"}) is True
|
|
614
|
+
assert _est_finding_securite({"security_relevant": 0}) is False
|
|
615
|
+
assert _est_finding_securite({"security_relevant": 42}) is True
|
|
592
616
|
assert (
|
|
593
617
|
_est_finding_securite(
|
|
594
618
|
{
|
|
595
|
-
"security_relevant":
|
|
619
|
+
"security_relevant": "unknown",
|
|
596
620
|
"source": "phi-complexity",
|
|
597
|
-
"rule_id": "
|
|
621
|
+
"rule_id": "CYCLOMATIQUE",
|
|
598
622
|
}
|
|
599
623
|
)
|
|
600
624
|
is False
|
|
601
625
|
)
|
|
626
|
+
assert (
|
|
627
|
+
_est_finding_securite(
|
|
628
|
+
{
|
|
629
|
+
"security_relevant": "",
|
|
630
|
+
"source": "phi-complexity",
|
|
631
|
+
"rule_id": "CWE-79",
|
|
632
|
+
}
|
|
633
|
+
)
|
|
634
|
+
is True
|
|
635
|
+
)
|
|
636
|
+
assert (
|
|
637
|
+
_est_finding_securite(
|
|
638
|
+
{
|
|
639
|
+
"security_relevant": True,
|
|
640
|
+
"source": "phi-complexity",
|
|
641
|
+
"rule_id": "CYCLOMATIQUE",
|
|
642
|
+
}
|
|
643
|
+
)
|
|
644
|
+
is True
|
|
645
|
+
)
|
|
602
646
|
|
|
603
647
|
def test_est_finding_securite_fallback_par_source(self):
|
|
648
|
+
"""Quand security_relevant est ABSENT, la classification se fait
|
|
649
|
+
par source + rule_id. Couvre les CWE critiques (XSS, injection SQL,
|
|
650
|
+
format-string) et les annotations qualité phi."""
|
|
651
|
+
# CWE critiques sans clé security_relevant → True
|
|
652
|
+
assert (
|
|
653
|
+
_est_finding_securite({"source": "phi-complexity", "rule_id": "CWE-79"})
|
|
654
|
+
is True
|
|
655
|
+
), "CWE-79 (XSS) sans clé security_relevant doit être classé sécurité"
|
|
656
|
+
assert (
|
|
657
|
+
_est_finding_securite({"source": "phi-complexity", "rule_id": "CWE-89"})
|
|
658
|
+
is True
|
|
659
|
+
), "CWE-89 (SQL Injection) sans clé security_relevant doit être classé sécurité"
|
|
604
660
|
assert (
|
|
605
661
|
_est_finding_securite({"source": "phi-complexity", "rule_id": "CWE-134"})
|
|
606
662
|
is True
|
|
607
|
-
)
|
|
663
|
+
), "CWE-134 (Format String) doit être classé sécurité"
|
|
664
|
+
# Annotations qualité phi → False (pas de sécurité)
|
|
608
665
|
assert (
|
|
609
666
|
_est_finding_securite(
|
|
610
667
|
{"source": "phi-complexity", "rule_id": "CYCLOMATIQUE"}
|
|
611
668
|
)
|
|
612
669
|
is False
|
|
613
|
-
)
|
|
670
|
+
), "CYCLOMATIQUE doit rester une annotation qualité"
|
|
671
|
+
assert (
|
|
672
|
+
_est_finding_securite({"source": "phi-complexity", "rule_id": "NESTING"})
|
|
673
|
+
is False
|
|
674
|
+
), "NESTING doit rester une annotation qualité"
|
|
614
675
|
assert (
|
|
615
676
|
_est_finding_securite({"source": "Flawfinder", "rule_id": "FF1009"}) is True
|
|
616
677
|
)
|
|
617
678
|
|
|
679
|
+
def test_est_finding_securite_dict_minimal(self):
|
|
680
|
+
"""Un finding vide ou avec uniquement un rule_id doit déclencher le
|
|
681
|
+
fallback sûr (sécurité par défaut pour source inconnue)."""
|
|
682
|
+
assert _est_finding_securite({}) is True
|
|
683
|
+
assert _est_finding_securite({"rule_id": "CWE-79"}) is True
|
|
684
|
+
assert _est_finding_securite({"source": "phi-complexity"}) is False
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
class TestClassificationFindings:
|
|
688
|
+
def test_classer_finding_cwe(self):
|
|
689
|
+
classification = classer_finding(
|
|
690
|
+
{
|
|
691
|
+
"source": "phi-complexity",
|
|
692
|
+
"rule_id": "CWE-79",
|
|
693
|
+
"severity": "high",
|
|
694
|
+
"surface": "production",
|
|
695
|
+
}
|
|
696
|
+
)
|
|
697
|
+
assert classification["family"] == "security"
|
|
698
|
+
assert classification["category"] == "injection"
|
|
699
|
+
assert classification["priority"] == "P0"
|
|
700
|
+
assert classification["cwe"] == "CWE-79"
|
|
701
|
+
assert classification["learning"]["signature"] == "phi-complexity:CWE-79"
|
|
702
|
+
|
|
703
|
+
def test_classer_finding_quality(self):
|
|
704
|
+
classification = classer_finding(
|
|
705
|
+
{
|
|
706
|
+
"source": "phi-complexity",
|
|
707
|
+
"rule_id": "CYCLOMATIQUE",
|
|
708
|
+
"severity": "medium",
|
|
709
|
+
"surface": "production",
|
|
710
|
+
"security_relevant": False,
|
|
711
|
+
}
|
|
712
|
+
)
|
|
713
|
+
assert classification["family"] == "quality"
|
|
714
|
+
assert classification["category"] == "complexity"
|
|
715
|
+
assert classification["priority"] == "P4"
|
|
716
|
+
|
|
717
|
+
def test_classer_findings_reuse_registry(self):
|
|
718
|
+
findings = [
|
|
719
|
+
{
|
|
720
|
+
"source": "phi-complexity",
|
|
721
|
+
"rule_id": "CWE-79",
|
|
722
|
+
"severity": "critical",
|
|
723
|
+
"surface": "production",
|
|
724
|
+
}
|
|
725
|
+
]
|
|
726
|
+
registry = {"phi-complexity:CWE-79": {"decision": "quality", "basis": "memo"}}
|
|
727
|
+
classer_findings(findings, registry)
|
|
728
|
+
classification = findings[0]["classification"]
|
|
729
|
+
assert classification["family"] == "quality"
|
|
730
|
+
assert classification["priority"] == "P4"
|
|
731
|
+
assert classification["learning"]["reused"] is True
|
|
732
|
+
|
|
733
|
+
def test_classer_findings_reuse_registry_security(self):
|
|
734
|
+
findings = [
|
|
735
|
+
{
|
|
736
|
+
"source": "phi-complexity",
|
|
737
|
+
"rule_id": "CWE-89",
|
|
738
|
+
"severity": "high",
|
|
739
|
+
"surface": "production",
|
|
740
|
+
}
|
|
741
|
+
]
|
|
742
|
+
registry = {"phi-complexity:CWE-89": {"decision": "security", "basis": "memo"}}
|
|
743
|
+
classer_findings(findings, registry)
|
|
744
|
+
classification = findings[0]["classification"]
|
|
745
|
+
assert classification["family"] == "security"
|
|
746
|
+
assert classification["category"] == "injection"
|
|
747
|
+
assert classification["priority"] == "P0"
|
|
748
|
+
assert classification["cwe"] == "CWE-89"
|
|
749
|
+
assert classification["learning"]["reused"] is True
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
class TestExtractionCwe:
|
|
753
|
+
def test_extraire_cwe_borne(self):
|
|
754
|
+
assert _extraire_cwe("CWE-79") == "79"
|
|
755
|
+
assert _extraire_cwe("CWE-0") == "0"
|
|
756
|
+
assert _extraire_cwe("CWE-12345") == "12345"
|
|
757
|
+
assert _extraire_cwe("CWE-99999") == "99999"
|
|
758
|
+
assert _extraire_cwe("CWE-123456") is None
|
|
759
|
+
assert _extraire_cwe("CWE-") is None
|
|
760
|
+
assert _extraire_cwe("CWE-abc") is None
|
|
761
|
+
assert _extraire_cwe("prefix CWE-89 suffix") == "89"
|
|
762
|
+
|
|
618
763
|
|
|
619
764
|
# ────────────────────────────────────────────────────────
|
|
620
765
|
# TESTS — Couverture des branches manquantes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|