Scan report
Skidotus / maldet
Could an attacker exploit mistakes in this code?
Serious issues. Review the high-severity findings before using it.
- High
- 12
- Medium
- 11
- Low
- 25
Sources: Bandit, Semgrep, pip-audit
Does this code look like it is trying to cause harm?
Real issues worth reviewing before you rely on this code.
- High
- 16
- Medium
- 0
- Low
- 6
Sources: YARA, ClamAV, GuardDog, dependency checker
- Likely real
- 20
- Uncertain
- 11
- Likely noise
- 39
The risk classifier rates every finding for how likely it is to be a real issue. Likely noise still counts, just much less.
What we found, in plain English
20 findings likely need attention, 39 look like noise. Most notable: CVE found: CVE-2026-59951 in mcp==1.29.0 — ### Summary In affected versions, a Streamab... in requirements.txt (99% confidence).
Detections by engine
Score over time
Overall score each time this repository was scanned.
Findings
Highest severity first. Select a finding to see the code and what it means.
-
- Engine
- Dependency checker · CVEs, typosquats and risky installs
- Counts towards
- Supply chain
- File
- requirements.txt
- Likely real
- Yes, 99% confidence
CVE found: CVE-2026-59951 in mcp==1.29.0 — ### Summary In affected versions, a Streamable HTTP server in stateful mode (the default) never recl
mcp==1.29.0 -
- Engine
- Dependency checker · CVEs, typosquats and risky installs
- Counts towards
- Supply chain
- File
- requirements.txt
- Likely real
- Yes, 99% confidence
CVE found: CVE-2026-116686 in mcp==1.29.0 — ### Summary In affected versions, when a connected MCP server declared an `outputSchema` for a tool,
mcp==1.29.0 -
- Engine
- Dependency checker · CVEs, typosquats and risky installs
- Counts towards
- Supply chain
- File
- requirements.txt
- Likely real
- Yes, 99% confidence
CVE found: CVE-2026-114603 in opentelemetry-api==1.37.0 — ## Summary `TraceState.from_header` splits the inbound W3C `tracestate` header with a regular expres
opentelemetry-api==1.37.0 -
- Engine
- Dependency checker · CVEs, typosquats and risky installs
- Counts towards
- Supply chain
- File
- requirements.txt
- Likely real
- Yes, 98% confidence
CVE found: CVE-2026-116684 in mcp==1.29.0 — ### Summary In affected versions, the SDK's server-side bearer authentication had no way to check th
mcp==1.29.0 -
- Engine
- Dependency checker · CVEs, typosquats and risky installs
- Counts towards
- Supply chain
- File
- requirements.txt
- Likely real
- Yes, 98% confidence
CVE found: CVE-2026-116685 in mcp==1.29.0 — ### Summary In affected versions, the SDK's Streamable HTTP and SSE client transports followed HTTP
mcp==1.29.0 -
- Engine
- Dependency checker · CVEs, typosquats and risky installs
- Counts towards
- Supply chain
- File
- requirements.txt
- Likely real
- Yes, 97% confidence
CVE found: CVE-2026-114613 in msgpack==1.2.1 — ### Impact The msgpack-python C extension (`msgpack._cmsgpack`) leaks a decoded map value when dese
msgpack==1.2.1 -
- Engine
- Semgrep · Pattern-based code analysis
- Counts towards
- Vulnerability
- File
- /app.py line 534
- Likely real
- Yes, 53% confidence
Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
cursor.execute(f""" SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY tool ORDER BY {SEVERITY_RANK} DESC, confidence DESC, id ASC ) AS rn FROM scan_results WHERE repo_id = %s ) ranked WHERE rn <= %s ORDER BY {SEVERITY_RANK} DESC, confidence DESC, tool ASC """, (repo_id, FINDINGS_PER_TOOL_LIMIT)) -
- Engine
- Semgrep · Pattern-based code analysis
- Counts towards
- Vulnerability
- File
- /backfill_summaries.py line 64
- Likely real
- Yes, 53% confidence
Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
cur.execute(sql, params) -
- Engine
- Semgrep · Pattern-based code analysis
- Counts towards
- Vulnerability
- File
- /backfill_summaries.py line 78
- Likely real
- Yes, 53% confidence
Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
cur.execute(f""" SELECT tool, -- COLLATE utf8mb4_bin, because the column's own collation is -- utf8mb4_0900_ai_ci -- case- and accent-insensitive. Python -- dict keys are not, so MySQL merged groups that -- _rank_groups() keeps apart: 17 issue_texts corpus-wide, -- including "Possible hardcoded password: 'abc'" with -- "...'ABC'", which are different hardcoded passwords. issue_text COLLATE utf8mb4_bin AS issue_text, COUNT(*) AS count, MAX({SEVERITY_RANK_SQL}) AS sev_rank, -- MAX(confidence), not MAX(COALESCE(confidence, 1.0)): -- COALESCE with a decimal literal promotes the FLOAT column -- to double, so 0.4406 comes back as 0.4406000077724457 and -- near-tied groups then sort differently from the scan-time -- path. NULLs are counted separately instead and folded in -- below, reproducing _rank_groups()' "NULL means 1.0". MAX(confidence) AS confidence, SUM(confidence IS NULL) AS null_conf, SUBSTRING_INDEX(GROUP_CONCAT( CONCAT(COALESCE(filename, ''), 0x1e, COALESCE(line_number, 0)) ORDER BY {SEVERITY_RANK_SQL} DESC, confidence DESC, -- utf8mb4_bin so the tie-break orders by byte, -- the way Python compares strings. Under the -- column's case-insensitive collation 'C' sorts -- after 'a', so MySQL picked acceslibre.py where -- Python picked CONTRIBUTING.md from the same -- group. COALESCE(filename, '') COLLATE utf8mb4_bin ASC, COALESCE(line_number, 0) ASC SEPARATOR 0x1f), 0x1f, 1) AS example FROM scan_results WHERE repo_id = %s GROUP BY tool, issue_text COLLATE utf8mb4_bin """, (repo_id,)) -
- Engine
- Semgrep · Pattern-based code analysis
- Counts towards
- Vulnerability
- File
- /db/export_seed.py line 67
- Likely real
- Yes, 53% confidence
Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
cur.execute(f""" SELECT r.id, r.owner, r.repo_name, rs.vuln_level, rs.malware_level, (SELECT COUNT(*) FROM scan_results sr WHERE sr.repo_id = r.id) AS n_findings FROM repositories r JOIN risk_scores rs ON rs.repo_id = r.id HAVING n_findings BETWEEN 1 AND {MAX_FINDINGS_PER_REPO} ORDER BY n_findings ASC """) -
- Engine
- Semgrep · Pattern-based code analysis
- Counts towards
- Vulnerability
- File
- /db/export_seed.py line 96
- Likely real
- Yes, 53% confidence
Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
cur.execute(f"SELECT * FROM {table} WHERE {key} IN ({placeholders})", repo_ids) -
- Engine
- Semgrep · Pattern-based code analysis
- Counts towards
- Vulnerability
- File
- /evaluate_summaries.py line 210
- Likely real
- Yes, 53% confidence
Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.
cur.execute(f""" SELECT r.id, CONCAT(r.owner,'/',r.repo_name) AS full_name, rs.risk_level, rs.vuln_level, rs.vuln_score, rs.malware_level, rs.malware_score, rs.llm_summary FROM repositories r JOIN risk_scores rs ON rs.repo_id = r.id WHERE {having} ORDER BY rs.final_score DESC """) -
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /rules.yar
- Likely real
- Uncertain, 39% confidence
YARA rule matched: detect_reverse_shell — Reverse shell command patterns
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /rules.yar
- Likely real
- Uncertain, 36% confidence
YARA rule matched: detect_crypto_miner — Cryptocurrency mining pool connection patterns
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /rules.yar
- Likely real
- Uncertain, 36% confidence
YARA rule matched: detect_process_injection — Windows process-injection API sequence
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /rules.yar
- Likely real
- Uncertain, 33% confidence
YARA rule matched: detect_credential_harvester — References credential/secret material by name AND a deliberate outbound-send API in the same file - narrower than \'reads any env var\', which large legitimate codebases do constantly regardless of intent. Still not proof the two are connected (YARA can\'t trace data flow) - a known limitation, see NOTES.md
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /rules.yar
- Likely real
- Probably noise, 18% confidence
YARA rule matched: detect_anti_analysis — Checks for a debugger, VM, or sandbox before continuing - a common malware evasion technique, essentially never present in ordinary application code
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /rules.yar
- Likely real
- Probably noise, 3% confidence
YARA rule matched: detect_base64_exec — Encoded payload decoded and then executed
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /llm_summary.py
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /model/risk_classifier.pkl
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /eval_sample.json
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /dep_checker.py
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /scanner.py
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /README.md
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /design/font-options.html
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /rules.yar
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_download_and_execute — Downloads a remote resource AND executes it - the actually dangerous combination, not just downloading alone
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /rules.yar
- Likely real
- Probably noise, 1% confidence
YARA rule matched: detect_obfuscated_string_execution — Reconstructs a string character-by-character or from hex escapes, then immediately executes it - fromCharCode/chr() alone are common in ordinary text-processing code, so this requires both the reconstruction and an execution call together
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /rules.yar
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_persistence_mechanism — Modifies a startup/scheduling location - legitimate application code essentially never does this
-
- Engine
- Semgrep · Pattern-based code analysis
- Counts towards
- Vulnerability
- File
- /scanner.py line 37
- Likely real
- Yes, 100% confidence
These permissions `0o700` are widely permissive and grant access to more people than may be necessary. A good default is `0o644` which gives read and write access to yourself and read access to everyone else.
os.chmod(path, 0o700) -
- Engine
- Semgrep · Pattern-based code analysis
- Counts towards
- Vulnerability
- File
- /scanner.py line 124
- Likely real
- Yes, 96% confidence
Hardcoded password is used as a default argument to 'extract_archives'. This could be dangerous if a real password is not supplied.
def extract_archives(repo_path, password="infected"): print(" Extracting archives...") count = 0 for root, dirs, files in os.walk(repo_path): for file in files: # lower() so an uppercase/mixed-case extension (.ZIP, .Rar) isn't # skipped — a trivial evasion otherwise, and malicious archives # are exactly where that matters. '.rar' was previously written # as 'rar' with no dot, which matched any filename ending in # those three letters rather than the extension. if file.lower().endswith(('.zip', '.7z', '.rar')): filepath = os.path.join(root,file) ext = file.rsplit('.', 1)[-1] extract_dir = filepath.replace(f'.{ext}' , '_extracted') try: result = subprocess.run( ['7z' , 'x' , f'-p{password}', filepath, f'-o{extract_dir}' , '-y'], capture_output=True, text=True, timeout=30 ) if result.returncode == 0: count += 1 print(f" Extracted: {file}") else: print(f" Skipped: {file}") except Exception as e: print(f" Archive error ({file}): {e}") print(f" Extracted {count} archive(s)") -
- Engine
- Semgrep · Pattern-based code analysis
- Counts towards
- Vulnerability
- File
- /db/export_seed.py line 67
- Likely real
- Yes, 52% confidence
Detected possible formatted SQL query. Use parameterized queries instead.
cur.execute(f""" SELECT r.id, r.owner, r.repo_name, rs.vuln_level, rs.malware_level, (SELECT COUNT(*) FROM scan_results sr WHERE sr.repo_id = r.id) AS n_findings FROM repositories r JOIN risk_scores rs ON rs.repo_id = r.id HAVING n_findings BETWEEN 1 AND {MAX_FINDINGS_PER_REPO} ORDER BY n_findings ASC """) -
- Engine
- Semgrep · Pattern-based code analysis
- Counts towards
- Vulnerability
- File
- /evaluate_summaries.py line 210
- Likely real
- Yes, 52% confidence
Detected possible formatted SQL query. Use parameterized queries instead.
cur.execute(f""" SELECT r.id, CONCAT(r.owner,'/',r.repo_name) AS full_name, rs.risk_level, rs.vuln_level, rs.vuln_score, rs.malware_level, rs.malware_score, rs.llm_summary FROM repositories r JOIN risk_scores rs ON rs.repo_id = r.id WHERE {having} ORDER BY rs.final_score DESC """) -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /db/export_seed.py line 96
- Likely real
- Uncertain, 46% confidence
Possible SQL injection vector through string-based query construction.
95 placeholders = ",".join(["%s"] * len(repo_ids)) 96 cur.execute(f"SELECT * FROM {table} WHERE {key} IN ({placeholders})", repo_ids) 97 rows = cur.fetchall() -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /backfill_summaries.py line 52
- Likely real
- Uncertain, 46% confidence
Possible SQL injection vector through string-based query construction.
51 52 sql = f""" 53 SELECT r.id, CONCAT(r.owner, '/', r.repo_name) AS full_name, 54 rs.vuln_score, rs.vuln_level, rs.malware_score, rs.malware_level, 55 rs.risk_level 56 FROM repositories r 57 JOIN risk_scores rs ON rs.repo_id = r.id 58 WHERE {' AND '.join(where)} 59 ORDER BY rs.final_score DESC 60 """ 61 if limit: -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /db/export_seed.py line 104
- Likely real
- Uncertain, 46% confidence
Possible SQL injection vector through string-based query construction.
103 statements = [ 104 f"INSERT INTO `{table}` ({col_list}) VALUES " 105 f"({', '.join(sql_literal(row[c]) for c in columns)});" 106 for row in rows -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /app.py line 534
- Likely real
- Uncertain, 46% confidence
Possible SQL injection vector through string-based query construction.
533 534 cursor.execute(f""" 535 SELECT * FROM ( 536 SELECT *, ROW_NUMBER() OVER ( 537 PARTITION BY tool 538 ORDER BY {SEVERITY_RANK} DESC, confidence DESC, id ASC 539 ) AS rn 540 FROM scan_results 541 WHERE repo_id = %s 542 ) ranked 543 WHERE rn <= %s 544 ORDER BY {SEVERITY_RANK} DESC, confidence DESC, tool ASC 545 """, (repo_id, FINDINGS_PER_TOOL_LIMIT)) 546 findings = cursor.fetchall() -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /backfill_summaries.py line 78
- Likely real
- Uncertain, 46% confidence
Possible SQL injection vector through string-based query construction.
77 cur.execute("SET SESSION group_concat_max_len = 1024") 78 cur.execute(f""" 79 SELECT tool, 80 -- COLLATE utf8mb4_bin, because the column's own collation is 81 -- utf8mb4_0900_ai_ci -- case- and accent-insensitive. Python 82 -- dict keys are not, so MySQL merged groups that 83 -- _rank_groups() keeps apart: 17 issue_texts corpus-wide, 84 -- including "Possible hardcoded password: 'abc'" with 85 -- "...'ABC'", which are different hardcoded passwords. 86 issue_text COLLATE utf8mb4_bin AS issue_text, 87 COUNT(*) AS count, 88 MAX({SEVERITY_RANK_SQL}) AS sev_rank, 89 -- MAX(confidence), not MAX(COALESCE(confidence, 1.0)): 90 -- COALESCE with a decimal literal promotes the FLOAT column 91 -- to double, so 0.4406 comes back as 0.4406000077724457 and 92 -- near-tied groups then sort differently from the scan-time 93 -- path. NULLs are counted separately instead and folded in 94 -- below, reproducing _rank_groups()' "NULL means 1.0". 95 MAX(confidence) AS confidence, 96 SUM(confidence IS NULL) AS null_conf, 97 SUBSTRING_INDEX(GROUP_CONCAT( 98 CONCAT(COALESCE(filename, ''), 0x1e, COALESCE(line_number, 0)) 99 ORDER BY {SEVERITY_RANK_SQL} DESC, confidence DESC, 100 -- utf8mb4_bin so the tie-break orders by byte, 101 -- the way Python compares strings. Under the 102 -- column's case-insensitive collation 'C' sorts 103 -- after 'a', so MySQL picked acceslibre.py where 104 -- Python picked CONTRIBUTING.md from the same 105 -- group. 106 COALESCE(filename, '') COLLATE utf8mb4_bin ASC, 107 COALESCE(line_number, 0) ASC 108 SEPARATOR 0x1f), 0x1f, 1) AS example 109 FROM scan_results 110 WHERE repo_id = %s 111 GROUP BY tool, issue_text COLLATE utf8mb4_bin 112 """, (repo_id,)) 113 -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /db/export_seed.py line 67
- Likely real
- Uncertain, 46% confidence
Possible SQL injection vector through string-based query construction.
66 within each level so the dump stays compact.""" 67 cur.execute(f""" 68 SELECT r.id, r.owner, r.repo_name, rs.vuln_level, rs.malware_level, 69 (SELECT COUNT(*) FROM scan_results sr WHERE sr.repo_id = r.id) AS n_findings 70 FROM repositories r 71 JOIN risk_scores rs ON rs.repo_id = r.id 72 HAVING n_findings BETWEEN 1 AND {MAX_FINDINGS_PER_REPO} 73 ORDER BY n_findings ASC 74 """) 75 eligible = cur.fetchall() -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /evaluate_summaries.py line 210
- Likely real
- Uncertain, 46% confidence
Possible SQL injection vector through string-based query construction.
209 "EXISTS (SELECT 1 FROM scan_results s WHERE s.repo_id = r.id)" 210 cur.execute(f""" 211 SELECT r.id, CONCAT(r.owner,'/',r.repo_name) AS full_name, 212 rs.risk_level, rs.vuln_level, rs.vuln_score, 213 rs.malware_level, rs.malware_score, rs.llm_summary 214 FROM repositories r JOIN risk_scores rs ON rs.repo_id = r.id 215 WHERE {having} 216 ORDER BY rs.final_score DESC 217 """) 218 rows = cur.fetchall() -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /config.example.py line 4
- Likely real
- Yes, 95% confidence
Possible hardcoded password: 'your_github_token_here'
3 4 GITHUB_TOKEN = "your_github_token_here" 5 DB_HOST = "localhost" -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 863
- Likely real
- Yes, 95% confidence
Possible hardcoded password: 'infected'
862 863 def scan_repo(repo, archive_password="infected", on_progress=None): 864 print(f"\n{'='*50}\nScanning: {repo}\n{'='*50}") 865 866 def report(stage): 867 if on_progress: 868 on_progress(stage) 869 870 report("Fetching repository info") 871 repo_info = get_repo_info(repo) 872 873 report("Cloning repository") 874 path = clone_repo(repo) 875 876 try: 877 report("Extracting archives") 878 extract_archives(path, archive_password) 879 880 findings = [] 881 882 report("Running Bandit") 883 findings += run_bandit(path) 884 885 report("Running Semgrep") 886 findings += run_semgrep(path) 887 888 report("Running YARA") 889 findings += run_yara(path) 890 891 report("Running ClamAV") 892 findings += run_clamav(path) 893 894 report("Running GuardDog") 895 findings += run_guarddog(path) 896 897 report("Checking dependencies") 898 findings += check_dependencies(path) 899 900 report("Filtering results") 901 findings = filter_noise(findings) 902 print(f" Findings after filter: {len(findings)}") 903 904 report("Scoring findings") 905 findings = score_findings(findings) 906 907 report("Calculating risk score") 908 high, medium, low, score, level = calculate_risk(findings) 909 category_risk = calculate_category_risk(findings) 910 911 # The LLM deliberately does NOT run here. Generation is 20-60s on 912 # CPU, and inside the scan that is 20-60s the visitor spends staring 913 # at a progress bar after the results already exist. The scan saves 914 # and returns; whoever called it generates the summary afterwards and 915 # the detail page shows a placeholder until it lands (see 916 # worker.py and app.py's /api/summary route). 917 report("Saving results") 918 repo_id = save_to_db(repo_info, findings, 919 high, medium, low, score, level, category_risk) 920 921 finally: 922 # clear temp folder 923 if os.path.exists(path): 924 shutil.rmtree(path) 925 926 print(f"\n Risk Score : {score}") 927 print(f" Risk Level : {level}") 928 929 return { 930 "repo_id": repo_id, 931 "name": repo_info["name"], 932 "owner": repo_info["owner"], 933 "language": repo_info["language"], 934 "stars": repo_info["stars"], 935 "high": high, 936 "medium": medium, 937 "low": low, 938 "score": score, 939 "risk_level": level, 940 "archive_password": archive_password 941 } -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 124
- Likely real
- Yes, 95% confidence
Possible hardcoded password: 'infected'
123 124 def extract_archives(repo_path, password="infected"): 125 print(" Extracting archives...") 126 count = 0 127 for root, dirs, files in os.walk(repo_path): 128 for file in files: 129 # lower() so an uppercase/mixed-case extension (.ZIP, .Rar) isn't 130 # skipped — a trivial evasion otherwise, and malicious archives 131 # are exactly where that matters. '.rar' was previously written 132 # as 'rar' with no dot, which matched any filename ending in 133 # those three letters rather than the extension. 134 if file.lower().endswith(('.zip', '.7z', '.rar')): 135 filepath = os.path.join(root,file) 136 ext = file.rsplit('.', 1)[-1] 137 extract_dir = filepath.replace(f'.{ext}' , '_extracted') 138 try: 139 result = subprocess.run( 140 ['7z' , 'x' , f'-p{password}', 141 filepath, f'-o{extract_dir}' , '-y'], 142 capture_output=True, text=True, timeout=30 143 ) 144 if result.returncode == 0: 145 count += 1 146 print(f" Extracted: {file}") 147 else: 148 print(f" Skipped: {file}") 149 except Exception as e: 150 print(f" Archive error ({file}): {e}") 151 print(f" Extracted {count} archive(s)") 152 -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /config.example.py line 20
- Likely real
- Yes, 95% confidence
Possible hardcoded password: 'your_db_password'
19 DB_USER = "fypuser" 20 DB_PASSWORD = "your_db_password" 21 DB_NAME = "fyp_scanner" -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 3
- Likely real
- Probably noise, 13% confidence
Consider possible security implications associated with the subprocess module.
2 import re 3 import subprocess 4 import shutil -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /dep_checker.py line 5
- Likely real
- Probably noise, 13% confidence
Consider possible security implications associated with the subprocess module.
4 import json 5 import subprocess 6 import tempfile -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /rescan_yara_dep.py line 15
- Likely real
- Probably noise, 12% confidence
Consider possible security implications associated with the subprocess module.
14 import tempfile 15 import subprocess 16 import sys -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 424
- Likely real
- Probably noise, 12% confidence
Starting a process with a partial executable path
423 try: 424 result = subprocess.run( 425 ["clamscan", "-r", path, "--no-summary"], 426 capture_output=True, text=True, timeout=120 427 ) 428 for line in result.stdout.strip().split("\n"): -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 392
- Likely real
- Probably noise, 12% confidence
Starting a process with a partial executable path
391 # there doesn't need a code change here. 392 result = subprocess.run( 393 ["yara", "-m", "-r", rules_file, path], 394 capture_output=True, text=True, timeout=120 395 ) 396 for line in result.stdout.strip().split("\n"): -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 343
- Likely real
- Probably noise, 12% confidence
Starting a process with a partial executable path
342 try: 343 result = subprocess.run( 344 ["semgrep", "--config=auto", path, "--json", "--quiet", 345 # Measured at 1,976MB peak on a trivial directory -- the largest 346 # single consumer in the pipeline, against 4GB on the deployment 347 # VPS. Semgrep skips files it cannot fit in this budget rather 348 # than failing, so the cost of the cap is a few unscanned large 349 # files instead of the OOM killer taking out MySQL mid-scan. 350 "--max-memory", str(SEMGREP_MAX_MEMORY_MB)], 351 capture_output=True, text=True, timeout=300 352 ) 353 -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 159
- Likely real
- Probably noise, 12% confidence
Starting a process with a partial executable path
158 try: 159 result = subprocess.run( 160 ["bandit", "-r", path, "-f", "json", "-q"], 161 capture_output=True, text=True, timeout=120 162 ) 163 data = json.loads(result.stdout) -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 139
- Likely real
- Probably noise, 12% confidence
Starting a process with a partial executable path
138 try: 139 result = subprocess.run( 140 ['7z' , 'x' , f'-p{password}', 141 filepath, f'-o{extract_dir}' , '-y'], 142 capture_output=True, text=True, timeout=30 143 ) 144 if result.returncode == 0: -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 104
- Likely real
- Probably noise, 12% confidence
Starting a process with a partial executable path
103 try: 104 result = subprocess.run( 105 ["git", "clone", "--depth=1", 106 f"https://github.com/{repo}.git", path], 107 capture_output=True, timeout=CLONE_TIMEOUT 108 ) 109 except subprocess.TimeoutExpired: -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /rescan_yara_dep.py line 44
- Likely real
- Probably noise, 12% confidence
Starting a process with a partial executable path
43 try: 44 result = subprocess.run( 45 ["git", "clone", "--depth=1", f"https://github.com/{r['owner']}/{r['repo_name']}.git", path], 46 capture_output=True, timeout=90 47 ) 48 if result.returncode != 0: -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /dep_checker.py line 320
- Likely real
- Probably noise, 12% confidence
Starting a process with a partial executable path
319 try: 320 result = subprocess.run( 321 ["pip-audit", "-r", req_path, "--format", "json", 322 # Never resolve or install anything: the repo under scan may be 323 # hostile, and pip-audit's project-path mode would invoke the 324 # project's own build backend, which is arbitrary code execution 325 # from untrusted source. Auditing a flat requirements file keeps 326 # this a pure lookup. 327 "--no-deps", "--disable-pip", "--progress-spinner", "off"], 328 capture_output=True, text=True, timeout=120 329 ) 330 return _parse_pip_audit_json(result.stdout, filename_for) -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 493
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
492 try: 493 result = subprocess.run(cmd, capture_output=True, text=True, 494 timeout=GUARDDOG_TIMEOUT) 495 # The kernel sandbox is on by default and aborts the scan where it -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /dep_checker.py line 320
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
319 try: 320 result = subprocess.run( 321 ["pip-audit", "-r", req_path, "--format", "json", 322 # Never resolve or install anything: the repo under scan may be 323 # hostile, and pip-audit's project-path mode would invoke the 324 # project's own build backend, which is arbitrary code execution 325 # from untrusted source. Auditing a flat requirements file keeps 326 # this a pure lookup. 327 "--no-deps", "--disable-pip", "--progress-spinner", "off"], 328 capture_output=True, text=True, timeout=120 329 ) 330 return _parse_pip_audit_json(result.stdout, filename_for) -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /rescan_yara_dep.py line 44
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
43 try: 44 result = subprocess.run( 45 ["git", "clone", "--depth=1", f"https://github.com/{r['owner']}/{r['repo_name']}.git", path], 46 capture_output=True, timeout=90 47 ) 48 if result.returncode != 0: -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 104
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
103 try: 104 result = subprocess.run( 105 ["git", "clone", "--depth=1", 106 f"https://github.com/{repo}.git", path], 107 capture_output=True, timeout=CLONE_TIMEOUT 108 ) 109 except subprocess.TimeoutExpired: -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 139
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
138 try: 139 result = subprocess.run( 140 ['7z' , 'x' , f'-p{password}', 141 filepath, f'-o{extract_dir}' , '-y'], 142 capture_output=True, text=True, timeout=30 143 ) 144 if result.returncode == 0: -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 159
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
158 try: 159 result = subprocess.run( 160 ["bandit", "-r", path, "-f", "json", "-q"], 161 capture_output=True, text=True, timeout=120 162 ) 163 data = json.loads(result.stdout) -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 343
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
342 try: 343 result = subprocess.run( 344 ["semgrep", "--config=auto", path, "--json", "--quiet", 345 # Measured at 1,976MB peak on a trivial directory -- the largest 346 # single consumer in the pipeline, against 4GB on the deployment 347 # VPS. Semgrep skips files it cannot fit in this budget rather 348 # than failing, so the cost of the cap is a few unscanned large 349 # files instead of the OOM killer taking out MySQL mid-scan. 350 "--max-memory", str(SEMGREP_MAX_MEMORY_MB)], 351 capture_output=True, text=True, timeout=300 352 ) 353 -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 392
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
391 # there doesn't need a code change here. 392 result = subprocess.run( 393 ["yara", "-m", "-r", rules_file, path], 394 capture_output=True, text=True, timeout=120 395 ) 396 for line in result.stdout.strip().split("\n"): -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 424
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
423 try: 424 result = subprocess.run( 425 ["clamscan", "-r", path, "--no-summary"], 426 capture_output=True, text=True, timeout=120 427 ) 428 for line in result.stdout.strip().split("\n"): -
- Engine
- Bandit · Python security linter
- Counts towards
- Vulnerability
- File
- /scanner.py line 500
- Likely real
- Probably noise, 3% confidence
subprocess call - check for execution of untrusted input.
499 if not result.stdout.strip(): 500 result = subprocess.run(cmd + ["--no-sandbox"], capture_output=True, 501 text=True, timeout=GUARDDOG_TIMEOUT) 502 data = json.loads(result.stdout) -
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /llm_summary.py
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_downloader — Downloads a remote resource - common and low-signal alone; see detect_download_and_execute for the composite version
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /rules.yar
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_downloader — Downloads a remote resource - common and low-signal alone; see detect_download_and_execute for the composite version
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /scanner.py
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_downloader — Downloads a remote resource - common and low-signal alone; see detect_download_and_execute for the composite version
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /dep_checker.py
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_downloader — Downloads a remote resource - common and low-signal alone; see detect_download_and_execute for the composite version
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /eval_sample.json
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_downloader — Downloads a remote resource - common and low-signal alone; see detect_download_and_execute for the composite version
-
- Engine
- YARA · Malware signature rules
- Counts towards
- Malicious pattern
- File
- /model/risk_classifier.pkl
- Likely real
- Probably noise, 0% confidence
YARA rule matched: detect_downloader — Downloads a remote resource - common and low-signal alone; see detect_download_and_execute for the composite version
No findings match these filters.