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.
Files changed (87) hide show
  1. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/PKG-INFO +14 -1
  2. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/README.md +13 -0
  3. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/securite.py +182 -1
  4. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/PKG-INFO +14 -1
  5. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/pyproject.toml +1 -1
  6. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_securite.py +149 -4
  7. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/LICENSE +0 -0
  8. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/__init__.py +0 -0
  9. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/__main__.py +0 -0
  10. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/akasha.py +0 -0
  11. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/analyseur.py +0 -0
  12. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/audit_slo.py +0 -0
  13. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/autosuture.py +0 -0
  14. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/__init__.py +0 -0
  15. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/asm_light.py +0 -0
  16. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/base.py +0 -0
  17. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/c_rust_light.py +0 -0
  18. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/elf_light.py +0 -0
  19. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backends/python.py +0 -0
  20. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/backup.py +0 -0
  21. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/bmad.py +0 -0
  22. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/canvas.py +0 -0
  23. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/cli.py +0 -0
  24. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/commit_risk.py +0 -0
  25. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/consensus.py +0 -0
  26. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/core.py +0 -0
  27. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/coverage_reporting.py +0 -0
  28. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/fingerprint.py +0 -0
  29. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/gnose.py +0 -0
  30. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/harvest.py +0 -0
  31. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/impossible.py +0 -0
  32. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/log_parser.py +0 -0
  33. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/metadata_ops.py +0 -0
  34. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/metriques.py +0 -0
  35. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/notebook_helpers.py +0 -0
  36. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/oracle.py +0 -0
  37. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/rapport.py +0 -0
  38. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/remediation.py +0 -0
  39. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/search.py +0 -0
  40. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/__init__.py +0 -0
  41. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/bayesian.py +0 -0
  42. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/behavior.py +0 -0
  43. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/host.py +0 -0
  44. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/response.py +0 -0
  45. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/sentinel/telemetry.py +0 -0
  46. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/suture.py +0 -0
  47. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity/vault.py +0 -0
  48. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/SOURCES.txt +0 -0
  49. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/dependency_links.txt +0 -0
  50. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/entry_points.txt +0 -0
  51. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/requires.txt +0 -0
  52. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/phi_complexity.egg-info/top_level.txt +0 -0
  53. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/setup.cfg +0 -0
  54. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_akasha.py +0 -0
  55. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_analyseur.py +0 -0
  56. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_asm_backend.py +0 -0
  57. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_audit_slo.py +0 -0
  58. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_autosuture.py +0 -0
  59. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_backends.py +0 -0
  60. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_backup.py +0 -0
  61. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_bmad.py +0 -0
  62. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_canvas.py +0 -0
  63. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_cli.py +0 -0
  64. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_commit_risk.py +0 -0
  65. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_consensus.py +0 -0
  66. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_core.py +0 -0
  67. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_coverage_reporting.py +0 -0
  68. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_elf_backend.py +0 -0
  69. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_engine.py +0 -0
  70. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_fingerprint.py +0 -0
  71. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_gnose.py +0 -0
  72. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_harvest.py +0 -0
  73. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_impossible.py +0 -0
  74. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_log_parser.py +0 -0
  75. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_main.py +0 -0
  76. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_metadata_ops.py +0 -0
  77. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_metriques.py +0 -0
  78. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_notebook_helpers.py +0 -0
  79. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_oracle.py +0 -0
  80. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_public_api_contract.py +0 -0
  81. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_rapport.py +0 -0
  82. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_remediation.py +0 -0
  83. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_scan_cli.py +0 -0
  84. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_search.py +0 -0
  85. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_sentinel.py +0 -0
  86. {phi_complexity-0.2.1 → phi_complexity-0.2.2}/tests/test_suture.py +0 -0
  87. {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.1
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
- return bool(finding.get("security_relevant"))
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.1
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).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "phi-complexity"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Code quality metrics based on Golden Ratio (φ) mathematical invariants"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -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": False,
619
+ "security_relevant": "unknown",
596
620
  "source": "phi-complexity",
597
- "rule_id": "CWE-134",
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