syfscan 0.1.0__py3-none-any.whl

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.
ia/__init__.py ADDED
File without changes
@@ -0,0 +1,121 @@
1
+ # ia/claude_summarizer.py
2
+ import anthropic
3
+ from dotenv import load_dotenv
4
+ from rich.console import Console
5
+ from rich.rule import Rule
6
+ import os
7
+
8
+ load_dotenv()
9
+
10
+ client = anthropic.Anthropic()
11
+
12
+ console = Console()
13
+
14
+
15
+ def generate_summary(report):
16
+ vulnerabilities_text = build_vulnerabilities_text(report)
17
+
18
+ if not vulnerabilities_text:
19
+ return None
20
+
21
+ message = client.messages.create(
22
+ model="claude-opus-4-6",
23
+ max_tokens=400,
24
+ messages=[
25
+ {
26
+ "role": "user",
27
+ "content": f"""You are a security expert. Analyze these vulnerabilities and respond in exactly this format:
28
+
29
+ HOW TO FIX
30
+ [One paragraph. List every package to upgrade and to which version.]
31
+
32
+ RISKS
33
+ [One paragraph. What could concretely happen if left unpatched.]
34
+
35
+ ADDITIONAL MEASURES
36
+ [3 bullet points max. Practical extra steps the developer can take.]
37
+
38
+ Vulnerabilities:
39
+ {vulnerabilities_text}"""
40
+ }
41
+ ]
42
+ )
43
+
44
+ return message.content[0].text
45
+
46
+
47
+ def display_summary(ai_text):
48
+ sections = {
49
+ "HOW TO FIX": ("πŸ”§ How to Fix", "green"),
50
+ "RISKS": ("⚠️ Risks", "red"),
51
+ "ADDITIONAL MEASURES": ("πŸ›‘οΈ Additional Measures", "yellow"),
52
+ }
53
+
54
+ console.print()
55
+ console.rule("[bold]AI Summary[/bold]")
56
+ console.print()
57
+
58
+ current_section = None
59
+ current_lines = []
60
+
61
+ for line in ai_text.splitlines():
62
+ line = line.strip()
63
+
64
+ matched = False
65
+ for key in sections:
66
+ if key in line.upper():
67
+ if current_section and current_lines:
68
+ title, color = sections[current_section]
69
+ content = "\n".join(current_lines).strip()
70
+ console.print(f"[bold {color}]{title}[/bold {color}]")
71
+ console.print(content)
72
+ console.print()
73
+
74
+ current_section = key
75
+ current_lines = []
76
+ matched = True
77
+ break
78
+
79
+ if not matched and line:
80
+ current_lines.append(line)
81
+
82
+ # Flush last section
83
+ if current_section and current_lines:
84
+ title, color = sections[current_section]
85
+ content = "\n".join(current_lines).strip()
86
+ console.print(f"[bold {color}]{title}[/bold {color}]")
87
+ console.print(content)
88
+ console.print()
89
+
90
+
91
+ def get_fixed_version(vuln):
92
+ try:
93
+ for affected in vuln.get("affected", []):
94
+ for r in affected.get("ranges", []):
95
+ for event in r.get("events", []):
96
+ if "fixed" in event:
97
+ return event["fixed"]
98
+ except (KeyError, IndexError):
99
+ pass
100
+ return None
101
+
102
+
103
+ def build_vulnerabilities_text(report):
104
+ lines = []
105
+
106
+ for dep in report:
107
+ if not dep["vulnerabilites"]:
108
+ continue
109
+ for vuln in dep["vulnerabilites"]:
110
+ fixed = get_fixed_version(vuln)
111
+ severity = vuln.get("database_specific", {}).get("severity", "UNKNOWN")
112
+ line = (
113
+ f"- {dep['nom']} {dep['version']} : "
114
+ f"{vuln.get('id', 'UNKNOWN')} | "
115
+ f"{vuln.get('summary', 'No description')} | "
116
+ f"Severity: {severity} | "
117
+ f"Fixed in: {fixed or 'unknown'}"
118
+ )
119
+ lines.append(line)
120
+
121
+ return "\n".join(lines)
output/__init__.py ADDED
File without changes
output/html.py ADDED
@@ -0,0 +1,96 @@
1
+ import json
2
+ from .terminal import get_label, get_score, score_couleur
3
+
4
+ # Aide IA pour cette fonction car orange3 n'existe pas en HTML, et orange n'offre pas le bon affichage au niveau du terminal
5
+ def convert_couleur_html(couleur):
6
+ map = {
7
+ "red":"red",
8
+ "yellow":"gold",
9
+ "orange3":"orange"
10
+ }
11
+ return map.get(couleur, "black")
12
+
13
+ def rapport_html (rapport, output_path, max_vulns):
14
+
15
+ #https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_span
16
+ #https://www-sololearn-com.translate.goog/en/Discuss/2715062/how-to-code-html-in-python?_x_tr_sl=en&_x_tr_tl=fr&_x_tr_hl=fr&_x_tr_pto=rq#
17
+
18
+ html="""
19
+ <!DOCTYPE html>
20
+ <html>
21
+ <head>
22
+ <meta charset ="UTF-8">
23
+ <style>
24
+
25
+ body {
26
+ background-color: white;
27
+ font-family: arial;
28
+ }
29
+ h1{
30
+ color: black
31
+ }
32
+ h2{
33
+ color: darkred
34
+ }
35
+ p{
36
+ color: black
37
+ }
38
+ </style>
39
+ </head>
40
+ <body>
41
+ <h1>Rapport syfscan </h1>
42
+ """
43
+
44
+
45
+
46
+ for dep in rapport:
47
+ nom = dep["nom"]
48
+ version = dep["version"]
49
+ vulns = dep["vulnerabilites"]
50
+
51
+ if max_vulns is not None:
52
+ vulns = vulns[:max_vulns]
53
+ couleur_h2 = "green" if not vulns else"darkred"
54
+
55
+ html+=f"<h2 style='color:{couleur_h2}'>{nom} {version}</h2>"
56
+
57
+ if not vulns:
58
+ html+=f"<p>Aucune vulnΓ©rabilitΓ© dΓ©tectΓ©e</p>"
59
+ continue
60
+
61
+
62
+ html+=f"<p>{len(vulns)} vulnΓ©rabilitΓ©s dΓ©tectΓ©es"
63
+ for v in vulns:
64
+
65
+ score = get_score(v)
66
+ couleur = convert_couleur_html(score_couleur(score))
67
+ cve = get_label(v)
68
+ summary = v.get("summary", "no description")
69
+
70
+ html+=f"""
71
+ <p>
72
+ <span style="color:{couleur}; font-weight:bold">{cve}
73
+ </span>
74
+ > Score: {score} <br>
75
+ {summary}
76
+ </p>
77
+ """
78
+
79
+
80
+
81
+ html+="""
82
+ </body>
83
+ </html>
84
+
85
+ """
86
+
87
+ with open(output_path, "w", encoding="utf-8") as f :
88
+ f.write(html)
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
output/json.py ADDED
@@ -0,0 +1,57 @@
1
+ import json
2
+ from .terminal import get_label, get_score, score_couleur
3
+
4
+ def rapport_json (rapport, output_path, max_vulns):
5
+
6
+ results =[]
7
+
8
+ for dep in rapport:
9
+ nom = dep["nom"]
10
+ version = dep["version"]
11
+ vulns = dep["vulnerabilites"]
12
+
13
+ if max_vulns is not None:
14
+ vulns = vulns[:max_vulns]
15
+
16
+ if not vulns:
17
+ results.append({
18
+ "nom": nom,
19
+ "version": version,
20
+ "statut" : "non vulnerable",
21
+ "nombre de vulnerabilite(s)": 0,
22
+
23
+ },)
24
+ continue
25
+
26
+ vulns_list=[]
27
+
28
+ for v in vulns:
29
+
30
+ score = get_score(v)
31
+ couleur = score_couleur(score)
32
+ cve = get_label(v)
33
+ summary = v.get("summary", "no description")
34
+ vulns_list.append({
35
+ "score": score,
36
+ "cve": cve,
37
+ "summary": summary,
38
+ },)
39
+
40
+ results.append({
41
+ "nom": nom,
42
+ "version": version,
43
+ "statut" : "vulnerable",
44
+ "nombre de vulnerabilite(s)": len(vulns),
45
+ "vulnerabilites": vulns_list
46
+ },)
47
+
48
+ with open(output_path, "w") as f :
49
+ json.dump(results, f, indent=4) #conversion en json de la liste results
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
output/pdf.py ADDED
@@ -0,0 +1,74 @@
1
+ from reportlab.lib.pagesizes import letter
2
+ from reportlab.pdfgen import canvas
3
+ from reportlab.lib import colors
4
+ from .terminal import get_label, get_score, score_couleur
5
+ def rapport_pdf(rapport, output_path, max_vulns=None):
6
+ c = canvas.Canvas(str(output_path), pagesize=letter)
7
+ width, height = letter
8
+
9
+ y = height - 40
10
+
11
+ c.setFont("Helvetica-Bold", 14)
12
+ c.drawString(40, y, "Rapport SyfScan - VulnΓ©rabilitΓ©s")
13
+
14
+ y -= 30
15
+
16
+ c.setFont("Helvetica", 10)
17
+
18
+ for dep in rapport:
19
+ nom = dep.get("nom")
20
+ version = dep.get("version")
21
+ vulns = dep.get("vulnerabilites", [])
22
+
23
+ if max_vulns:
24
+ vulns = vulns[:max_vulns]
25
+ if not vulns :
26
+ c.setFillColor(colors.green)
27
+ else:
28
+ c.setFillColor(colors.red)
29
+
30
+
31
+ c.setFont("Helvetica-Bold", 11)
32
+ c.drawString(40, y, f"{nom} ({version})")
33
+ y -= 15
34
+
35
+ if not vulns:
36
+
37
+ c.setFont("Helvetica", 10)
38
+ c.drawString(60, y, "Non vulnΓ©rable")
39
+ y -= 20
40
+ continue
41
+
42
+
43
+
44
+
45
+ c.setFont("Helvetica", 10)
46
+ c.drawString(60, y, f"{len(vulns)} vulnΓ©rabilitΓ©(s)")
47
+ y -= 15
48
+
49
+ for v in vulns:
50
+ #cve = v.get("id", "N/A")
51
+ cve=get_label(v)
52
+ summary = v.get("summary", "no description")
53
+ score = get_score(v)
54
+ couleur = score_couleur(score)
55
+ text = f"- {cve}: {summary[:60]}"
56
+
57
+ if couleur =="red":
58
+ c.setFillColor(colors.red)
59
+ elif couleur =="orange3":
60
+ c.setFillColor(colors.orange)
61
+ else:
62
+ c.setFillColor(colors.gold)
63
+
64
+ if y < 40:
65
+ c.showPage()
66
+ c.setFont("Helvetica", 10)
67
+ y = height - 40
68
+
69
+ c.drawString(80, y, text)
70
+ y -= 15
71
+
72
+ y -= 10
73
+
74
+ c.save()
output/terminal.py ADDED
@@ -0,0 +1,72 @@
1
+ from rich.console import Console
2
+
3
+ console = Console()
4
+
5
+ # https://ossf.github.io/osv-schema/#database_specific-field
6
+ def get_score(vuln):
7
+ # Si on a le score dans severity[]
8
+ for severity in vuln.get("severity", []):
9
+ if severity.get("type") in ("CVSS_V3", "CVSS_V2"):
10
+ try:
11
+ return float(severity.get("score", 0.0))
12
+ except ValueError:
13
+ pass
14
+
15
+ score_dictionnary = {"CRITICAL": 9.5, "HIGH": 8.0, "MEDIUM": 5.0, "LOW": 2.0}
16
+
17
+ # Si le score est dans database_specific
18
+ db_severity = vuln.get("database_specific", {}).get("severity", "")
19
+ if db_severity.upper() in score_dictionnary:
20
+ return score_dictionnary[db_severity.upper()]
21
+
22
+ # Si le score est dans affected[0].ecosystem_specific
23
+ affected = vuln.get("affected", [])
24
+ if affected:
25
+ eco_severity = affected[0].get("ecosystem_specific", {}).get("severity", "")
26
+ if eco_severity.upper() in score_dictionnary:
27
+ return score_dictionnary[eco_sev.upper()]
28
+
29
+ return 0.0
30
+
31
+
32
+ def score_couleur(score):
33
+ if score >= 9.0:
34
+ return "red"
35
+ elif score >= 7.0:
36
+ return "orange3"
37
+ else:
38
+ return "yellow"
39
+
40
+
41
+ # Fonction faite avec IA
42
+ def get_label(vuln):
43
+ aliases = vuln.get("aliases", [])
44
+ return next((a for a in aliases if a.startswith("CVE-")), vuln.get("id", "N/A"))
45
+
46
+
47
+ def display_report(rapport, max_vulns=None):
48
+ console.print("\n[bold green]// Rapport syfscan[/bold green]\n")
49
+
50
+ for dep in rapport:
51
+ nom = dep["nom"]
52
+ version = dep["version"]
53
+ vulns = dep["vulnerabilites"]
54
+
55
+ if max_vulns is not None:
56
+ vulns = vulns[:max_vulns]
57
+
58
+ if not vulns:
59
+ console.print(f"[green]{nom} {version} β€” 0 vulnΓ©rabilitΓ©[/green]")
60
+ console.print()
61
+ continue
62
+
63
+ console.print(f"[red]{nom} {version} β€” {len(vulns)} vulnΓ©rabilitΓ©(s)[/red]")
64
+
65
+ for v in vulns:
66
+ score = get_score(v)
67
+ couleur = score_couleur(score)
68
+ cve = get_label(v)
69
+ summary = v.get("summary", "no description")
70
+ console.print(f" [dim]>[/dim] [{couleur}]{cve} β€” {summary}[/{couleur}]")
71
+
72
+ console.print()
parsers/requirement.py ADDED
@@ -0,0 +1,41 @@
1
+ from packaging.requirements import Requirement
2
+ from rich.console import Console
3
+
4
+ console = Console()
5
+
6
+ def parse_requirements(fichier):
7
+ finale_liste =[]
8
+ try :
9
+ with open (fichier, "r") as f :
10
+ lignes = f.readlines()
11
+
12
+ for ligne in lignes :
13
+ ligne = ligne.strip()
14
+
15
+ if not ligne or ligne.startswith('#'):
16
+ continue
17
+
18
+ try :
19
+ req = Requirement(ligne)
20
+ nom = req.name
21
+ version ="0.0.0"
22
+
23
+ if req.specifier :#determiner la bonne version si elle existe (borne basse)
24
+ for spec in req.specifier :
25
+ if spec.operator in ["==", ">=", "~=", "==="]:
26
+ version =spec.version
27
+ break
28
+ finale_liste.append({"nom":nom, "version" :version})
29
+
30
+ except Exception :
31
+ console.print(f"[yellow][ WARNING ][/yellow] [dim]Ligne ignorΓ©e β€”[/dim] [cyan]{ligne}[/cyan]")
32
+
33
+
34
+
35
+
36
+ except Exception as e :
37
+ console.print(f"[bold red][ ERROR ][/bold red] [red]impossible de lire le fichier β€” {e}[/red]")
38
+ cons
39
+
40
+ console.print()
41
+ return finale_liste
scanner/__init__.py ADDED
File without changes
scanner/osv.py ADDED
@@ -0,0 +1,48 @@
1
+ import requests
2
+ from rich.console import Console
3
+
4
+ from output.terminal import get_score
5
+
6
+ console = Console()
7
+
8
+ OSV_API_URL = "https://api.osv.dev/v1/query"
9
+
10
+ def scan_dependencies(dependencies):
11
+
12
+ rapport = []
13
+
14
+ for dep in dependencies:
15
+ nom = dep["nom"]
16
+ version = dep["version"]
17
+
18
+ console.print(f"[green]>[/green] [dim]Scan[/dim] [cyan]{nom}[/cyan] [dim]{version}[/dim]")
19
+
20
+ try:
21
+ response = requests.post(OSV_API_URL, json={
22
+ "package": {
23
+ "name": nom,
24
+ "ecosystem": "PyPI"
25
+ },
26
+ "version": version
27
+ })
28
+ response.raise_for_status()
29
+ data = response.json()
30
+
31
+ vulns = sorted(data.get("vulns", []), key=get_score, reverse=True)
32
+
33
+ rapport.append({
34
+ "nom": nom,
35
+ "version": version,
36
+ "vulnerabilites": vulns
37
+ })
38
+
39
+ except requests.exceptions.RequestException as e:
40
+ console.print(f"[bold red][ ERROR ][/bold red] [red]{nom} β€” {e}[/red]")
41
+ rapport.append({
42
+ "nom": nom,
43
+ "version": version,
44
+ "vulnerabilites": [],
45
+ "erreur": str(e)
46
+ })
47
+
48
+ return rapport
syfscan/__init__.py ADDED
File without changes
syfscan/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from syfscan.cli import main
2
+
3
+ if __name__== "__main__":
4
+ main()
syfscan/cli.py ADDED
@@ -0,0 +1,120 @@
1
+ import argparse
2
+ import sys
3
+ from pathlib import Path
4
+ from rich.console import Console
5
+
6
+ from parsers.requirement import parse_requirements
7
+ from scanner.osv import scan_dependencies
8
+ from ia.claude_summarizer import generate_summary, display_summary
9
+ from output.terminal import display_report
10
+ from output.json import rapport_json
11
+ from output.html import rapport_html
12
+ from output.pdf import rapport_pdf
13
+
14
+ console = Console()
15
+
16
+ BANNER = """
17
+ [green]
18
+ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•— β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—
19
+ β–ˆβ–ˆβ•”β•β•β•β•β•β•šβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘
20
+ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘
21
+ β•šβ•β•β•β•β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ•”β•β•β• β•šβ•β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘
22
+ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘
23
+ β•šβ•β•β•β•β•β•β• β•šβ•β• β•šβ•β• β•šβ•β•β•β•β•β•β• β•šβ•β•β•β•β•β•β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β•β•β•
24
+ [/green][dim green] v1.0 β€” Python dependency vulnerability scanner. [/dim green]
25
+ """
26
+
27
+ def main():
28
+ # Fait avec https://docs.python.org/3/library/argparse.html
29
+ parser = argparse.ArgumentParser(
30
+ prog="syfscan",
31
+ description="SyfScan β€” DΓ©tecteur de vulnΓ©rabilitΓ©s de dΓ©pendances",
32
+ epilog="Exemple : syfscan requirements.txt"
33
+ )
34
+
35
+ parser.add_argument(
36
+ "file",
37
+ help="Chemin vers le fichier requirements.txt"
38
+ )
39
+
40
+ parser.add_argument(
41
+ "-xvuln",
42
+ type=int,
43
+ default=None,
44
+ metavar="X",
45
+ help="Afficher uniquement les X vulnΓ©rabilitΓ©s les plus graves (ex: -xvuln 5)"
46
+ )
47
+
48
+ parser.add_argument(
49
+ "--no-ai",
50
+ action="store_true",
51
+ help="DΓ©sactiver le rΓ©sumΓ© IA"
52
+ )
53
+
54
+ parser.add_argument(
55
+ "--json",
56
+ action="store_true",
57
+ help="GΓ©nΓ©rer un fichier au format JSON"
58
+ )
59
+
60
+ parser.add_argument(
61
+ "--html",
62
+ action="store_true",
63
+ help="GΓ©nΓ©rer un fichier au format HTML"
64
+ )
65
+
66
+ parser.add_argument(
67
+ "--pdf",
68
+ action="store_true",
69
+ help="GΓ©nΓ©rer un fichier au format PDF"
70
+ )
71
+
72
+
73
+ args = parser.parse_args()
74
+ file = Path(args.file)
75
+
76
+ console.print(BANNER)
77
+
78
+ # Verification validitΓ© des entrΓ©es clients
79
+ if not file.exists():
80
+ console.print(f"[bold red][ERREUR][/bold red] Fichier '{file}' introuvable.")
81
+ sys.exit(1)
82
+
83
+
84
+
85
+ if file.name != "requirements.txt":
86
+ console.print("[bold red][ERREUR][/bold red] SyfScan accepte uniquement les fichiers requirements.txt")
87
+ sys.exit(1)
88
+
89
+ # Parsing
90
+ console.print(f"[green]>[/green] [dim]Fichier cible :[/dim][cyan]{file}[/cyan]")
91
+ dependencies = parse_requirements(file) # fonction parse Γ  rΓ©cupΓ©rer dans parser/requirements.py
92
+
93
+
94
+ # Interrogation OSV.dev
95
+ report = scan_dependencies(dependencies) # fonction scan_dependencies Γ  rΓ©cupΓ©rer dans scanner/osv.py
96
+
97
+ # Output
98
+
99
+ #permet de choisir sous quel format exporter les donnΓ©es, par defaut dans le terminal
100
+ if args.json:
101
+ rapport_json(report, Path("rapport.json"), args.xvuln)
102
+ console.print("\n[bold green]Rapport JSON gΓ©nΓ©rΓ© :[/bold green] rapport.json\n")
103
+ elif args.html:
104
+ rapport_html(report, Path("rapport.html"), args.xvuln)
105
+ console.print("\n[bold green]Rapport HTML gΓ©nΓ©rΓ© :[/bold green] rapport.html\n")
106
+ elif args.pdf:
107
+ rapport_pdf(report, Path("rapport.pdf"), args.xvuln)
108
+ console.print("\n[bold green]Rapport PDF gΓ©nΓ©rΓ© :[/bold green] rapport.pdf\n")
109
+
110
+ else :
111
+ display_report(report, args.xvuln)
112
+
113
+ # RΓ©sumΓ© IA
114
+ if not args.no_ai:
115
+ print("Generating AI summary...")
116
+ ai_report = generate_summary(report)
117
+ display_summary(ai_report)
118
+
119
+ if __name__ == "__main__" :
120
+ main()
@@ -0,0 +1,4 @@
1
+ Metadata-Version: 2.4
2
+ Name: syfscan
3
+ Version: 0.1.0
4
+ Summary: DΓ©tecteur de vulnΓ©rabilitΓ©s de dΓ©pendances
@@ -0,0 +1,18 @@
1
+ ia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ia/claude_summarizer.py,sha256=bX2XOZZFZchMhFc1JX6Z5aeLjEyG9Eman7HHoKlFrQk,3385
3
+ output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ output/html.py,sha256=2WiqM__dQhE5u_WhwqPfY3yhzek00SHYB1D8OjTIgQg,2393
5
+ output/json.py,sha256=Yvf2lfhmArpPW3HIvkXrGSqAqCB6aSE1Uajtwvx_teI,1322
6
+ output/pdf.py,sha256=jNW2fLfwZpxGLefqfdjJz9Q8HO7556He2Te483cqgL0,1917
7
+ output/terminal.py,sha256=uK1vj1VNXgykBaVsuOeqKWf-mnZ8kENyvXti7Ag7ucQ,2216
8
+ parsers/requirement.py,sha256=pbJh-G-6StlNT3nPTWlh3Pr5Fe6CexFaS30uL70ykbQ,1390
9
+ scanner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ scanner/osv.py,sha256=ZyRbTxj_qgN7m2yf1fDPOe_nQbjLjOE4_8cK1QHPoyc,1302
11
+ syfscan/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ syfscan/__main__.py,sha256=WDqExrefwrXk1MOBmjwtXPnIihVito76Cd1vcfnFxTI,66
13
+ syfscan/cli.py,sha256=rNKvzelSr0ZIuK9askkfohhqBMtrVBHogMu7aheBEag,4266
14
+ syfscan-0.1.0.dist-info/METADATA,sha256=Ajv03QPBOORZq5gv_7wWc2uvprOSc7R9RQk8ur__zIM,107
15
+ syfscan-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
16
+ syfscan-0.1.0.dist-info/entry_points.txt,sha256=Xil9FSfre8F43iD4aBexk4GSMsKurKm5YhnSO_YC3wc,50
17
+ syfscan-0.1.0.dist-info/top_level.txt,sha256=PnPb582uRcmoEcQe3TcydY9IteXc-ZhlX0JzPJRNj6M,34
18
+ syfscan-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ syfscan = syfscan.__main__:main
@@ -0,0 +1,5 @@
1
+ ia
2
+ output
3
+ parsers
4
+ scanner
5
+ syfscan