• # audit

    Posté par (site web personnel, Mastodon) . En réponse au journal J'ai failli le faire. Évalué à 10.

    C'est un sujet que j’essaye de traiter/implémenter en ce moment en utilisant un script Python.

    A la fin, j'obtiens une note de 0 à 100.

    0 est fiable selon mes critères, non zéro, il faut se poser des questions.

    Le script:

    #!/usr/bin/env python3
    import zipfile
    import json
    import re
    import os
    import sys
    from collections import defaultdict
    # -----------------------------------------
    # YARA-like malicious patterns
    # -----------------------------------------
    yara_rules = {
     "eval_obfuscation": r"eval\s*\(",
     "function_constructor": r"new Function\s*\(",
     "xhr_suspicious": r"XMLHttpRequest\s*\(",
     "websocket": r"new WebSocket\s*\(",
     "crypto_mining": r"(CoinHive|cryptonight|miner)",
     "tracking_fingerprinting": r"(canvas\.toDataURL|audioContext|mediaDevices\.enumerateDevices)",
     "url_beacon": r"(navigator\.sendBeacon)",
     "shadow_dom_injection": r"(attachShadow)",
     "encoded_payload": r"(atob\(|btoa\()",
     "exfiltration_fetch": r"fetch\s*\([^)]*(http|https):\/\/",
     "suspicious_iframe": r"<iframe[^>]+src="
    }
    # -----------------------------------------
    # Sensitive Firefox permissions → explanations
    # -----------------------------------------
    permission_explanations = {
     "tabs": "Access to all browser tabs (privacy sensitive).",
     "cookies": "Can read/write cookies across sites.",
     "webRequest": "Can monitor/modify all network requests.",
     "webRequestBlocking": "Can block and alter network requests (highly sensitive).",
     "history": "Can read browsing history.",
     "downloads": "Full access to all downloads.",
     "nativeMessaging": "Can communicate with external apps (critical).",
     "management": "Can manage extensions (dangerous).",
     "proxy": "Can alter browser network proxy settings.",
     "storage": "Can store arbitrary data (may include tracking identifiers)."
    }
    # -----------------------------------------
    # Known tracker domains for filtering endpoints
    # -----------------------------------------
    TRACKERS = ["google-analytics", "doubleclick", "facebook", "fbcdn",
     "hotjar", "mixpanel", "matomo", "segment.io",
     "sentry", "amplitude", "snowplow"]
    # -----------------------------------------
    # Utility functions
    # -----------------------------------------
    def detect_minification(content):
     line_lengths = [len(line) for line in content.split("\n")]
     long_lines = sum(1 for l in line_lengths if l > 300)
     ratio = long_lines / max(1, len(line_lengths))
     return ratio > 0.30 # heuristic
    def detect_obfuscation(content):
     suspicious = 0
     if re.search(r"[A-Za-z_$]{1,2}\d{3,}", content):
     suspicious += 1
     if re.search(r"function\(\w,\w,\w\)\{", content):
     suspicious += 1
     if re.search(r"while\s*\(\!\!\[\]\)", content):
     suspicious += 1
     return suspicious >= 2
    def yara_scan(content):
     matches = []
     for name, pattern in yara_rules.items():
     if re.search(pattern, content, re.IGNORECASE):
     matches.append(name)
     return matches
    def extract_urls(content):
     pattern = r"https?://[A-Za-z0-9\.\-_/~\?#=%]+(?=[\s\"\'\)\]\}<>]|$)"
     return re.findall(pattern, content)
    def classify_urls(urls):
     classified = {"trackers": [], "api": [], "other": []}
     for u in urls:
     if any(t in u for t in TRACKERS):
     classified["trackers"].append(u)
     elif re.search(r"/api/|/v\d+/|token|auth", u):
     classified["api"].append(u)
     else:
     classified["other"].append(u)
     return classified
    # -----------------------------------------
    # Compute global risk score
    # -----------------------------------------
    def compute_risk_score(permissions, minified_files, obfuscated_files, yara_hits, urls):
     score = 0
     # Permissions
     if "<all_urls>" in permissions:
     score += 25
     if "webRequest" in permissions:
     score += 10
     if "cookies" in permissions:
     score += 10
     if "history" in permissions:
     score += 10
     if "nativeMessaging" in permissions:
     score += 20
     # File metrics
     score += len(minified_files) * 5
     score += len(obfuscated_files) * 10
     # YARA hits
     for hits in yara_hits.values():
     score += len(hits) * 10
     # URLs
     score += min(len(urls), 10) # up to 10 points
     # Cap at 100
     return min(score, 100)
    # -----------------------------------------
    # Main analysis
    # -----------------------------------------
    def analyze_xpi(file_path):
     report = []
     js_files = {}
     urls_found = set()
     yara_hits = defaultdict(list)
     minified_files = []
     obfuscated_files = []
     with zipfile.ZipFile(file_path, 'r') as z:
     # -------------------------
     # 1. MANIFEST EXTRACTION
     # -------------------------
     manifest_data = None
     for name in z.namelist():
     if name.endswith("manifest.json"):
     manifest_data = json.loads(z.read(name).decode("utf-8", errors="ignore"))
     break
     if manifest_data is None:
     report.append("No manifest.json found → invalid extension.")
     return "\n".join(report)
     # -------------------------
     # 2. Inspect manifest.json
     # -------------------------
     report.append("=== MANIFEST ANALYSIS ===\n")
     # Permissions
     permissions = manifest_data.get("permissions", [])
     host_permissions = manifest_data.get("host_permissions", [])
     report.append("Permissions declared:")
     if not permissions:
     report.append(" None")
     else:
     for perm in permissions:
     explanation = permission_explanations.get(perm, "No specific risk info.")
     report.append(f" - {perm}: {explanation}")
     report.append("\nHost permissions:")
     if not host_permissions:
     report.append(" None")
     else:
     for host in host_permissions:
     report.append(f" - {host}")
     # Data collection declaration
     dcp = manifest_data.get("data_collection_permissions", {})
     report.append("\nData collection permissions:")
     if dcp:
     report.append(f" Raw: {json.dumps(dcp, indent=4)}")
     if dcp.get("required") == ["none"]:
     report.append(" → The extension claims to collect NO DATA.")
     else:
     report.append(" No data collection declaration found.")
     report.append("\n")
     # -------------------------
     # 3. FILE CONTENT ANALYSIS
     # -------------------------
     report.append("=== FILE ANALYSIS ===\n")
     for name in z.namelist():
     if not name.endswith((".js", ".html", ".css")):
     continue
     content = z.read(name).decode("utf-8", errors="ignore")
     # JS only
     if name.endswith(".js"):
     js_files[name] = content
     # Minification detection
     if detect_minification(content):
     minified_files.append(name)
     # Obfuscation detection
     if detect_obfuscation(content):
     obfuscated_files.append(name)
     # YARA-like scan
     for hit in yara_scan(content):
     yara_hits[name].append(hit)
     # URL extraction (JS/HTML/CSS)
     for url in extract_urls(content):
     urls_found.add(url)
     # Minified
     report.append("Potentially minified files:")
     if minified_files:
     for f in minified_files:
     report.append(f" - {f}")
     else:
     report.append(" None")
     report.append("")
     # Obfuscated
     report.append("Potentially obfuscated files:")
     if obfuscated_files:
     for f in obfuscated_files:
     report.append(f" - {f}")
     else:
     report.append(" None")
     report.append("")
     # YARA hits
     report.append("YARA-like detection results:")
     if yara_hits:
     for f, hits in yara_hits.items():
     report.append(f" {f}: {', '.join(hits)}")
     else:
     report.append(" No suspicious patterns detected.")
     report.append("")
     # URLs
     classified = classify_urls(urls_found)
     report.append("External endpoints found:")
     for cat, urls in classified.items():
     report.append(f" {cat.capitalize()}:")
     if urls:
     for u in urls:
     report.append(f" - {u}")
     else:
     report.append(" None")
     report.append("")
     # -------------------------
     # 4. Global risk score
     # -------------------------
     risk_score = compute_risk_score(permissions, minified_files, obfuscated_files, yara_hits, urls_found)
     report.append(f"=== GLOBAL RISK SCORE: {risk_score}/100 ===\n")
     # -------------------------
     # 5. Final Privacy Warning
     # -------------------------
     report.append("=== PRIVACY RECOMMENDATION ===")
     report.append(
     "Always disable automatic updates for extensions when conducting security reviews. "
     "Updates may replace safe code with unsafe content without user confirmation."
     )
     return "\n".join(report)
    if __name__ == "__main__":
     if len(sys.argv) != 2:
     print("Usage: python3 audit_xpi.py extension.xpi")
     sys.exit(1)
     filePath = sys.argv[1]
     print(analyze_xpi(filePath))

    Le report me donne pour tampermonkey:

    === MANIFEST ANALYSIS ===
    Permissions declared:
     - alarms: No specific risk info.
     - notifications: No specific risk info.
     - tabs: Access to all browser tabs (privacy sensitive).
     - idle: No specific risk info.
     - webNavigation: No specific risk info.
     - webRequest: Can monitor/modify all network requests.
     - webRequestBlocking: Can block and alter network requests (highly sensitive).
     - unlimitedStorage: No specific risk info.
     - storage: Can store arbitrary data (may include tracking identifiers).
     - contextMenus: No specific risk info.
     - clipboardWrite: No specific risk info.
     - cookies: Can read/write cookies across sites.
     - downloads: Full access to all downloads.
     - <all_urls>: No specific risk info.
    Host permissions:
     None
    Data collection permissions:
     No data collection declaration found.
    === FILE ANALYSIS ===
    Potentially minified files:
     - content.js
     - extension.js
     - page.js
     - editor.js
     - lint.js
     - cache.js
     - background.js
     - vendor/saveas/filesaver.js
     - vendor/eslint/eslint.js
     - vendor/jsdiff/diff.js
    Potentially obfuscated files:
     - content.js
     - extension.js
     - editor.js
     - background.js
     - vendor/eslint/eslint.js
    YARA-like detection results:
     content.js: shadow_dom_injection, encoded_payload
     extension.js: function_constructor, crypto_mining, suspicious_iframe
     background.js: eval_obfuscation, function_constructor, crypto_mining, encoded_payload
     vendor/eslint/eslint.js: eval_obfuscation, function_constructor, tracking_fingerprinting
    External endpoints found:
     Trackers:
     - https://www.facebook.com/tampermonkey
     - https://a.tampermonkey.net/matomo.php
     - https://www.facebook.com/groups/SocialFixerUsersSupport/
     - https://www.facebook.com/fluffbustingpurity
     Api:
     - https://api.dropboxapi.com/2/auth/token/revoke
     - https://cloud-api.yandex.net/v1/disk/
     - https://www.googleapis.com/drive/v3/changes/startPageToken
     - https://www.googleapis.com/auth/drive.appdata
     - https://oauth.yandex.com/authorize
     - https://login.live.com/oauth20_authorize.srf
     - https://www.googleapis.com/drive/v3/files?
     - https://www.googleapis.com/drive/v3/changes/?
     - https://accounts.google.com/o/oauth2/v2/auth
     - https://www.googleapis.com/drive/v3/files/
     - https://www.dropbox.com/oauth2/authorize
     - https://accounts.tampermonkey.net/e/oauth2/v1
     Other:
     - https://eslint.org/docs/rules/no-process-env
     [...] filtered tons of eslint URLs
     - http://coffeescript.org/browser-compiler/coffeescript.js
     - https://eslint.org/docs/rules/no-new-native-nonconstructor
     - https://www.tampermonkey.net/faq#Q406
     - https://github.com/ajv-validator/ajv/blob/master/lib/definition_schema.js
     - https://greasyfork.org/scripts/
     - https://eslint.org/docs/rules/prefer-spread
     - https://eslint.org/docs/rules/no-undef-init
     - http://xkcd.com/970/
     - https://api.dropboxapi.com/2/files/delete
     - https://api.onedrive.com/v1.0/drive/special
     - https://forum.userstyles.org/post/discussion?Discussion/StyleID=
     - http://json-schema.org/draft-07/schema#
     - https://eslint.org/docs/rules/no-negated-condition
     - https://www.tampermonkey.net/privacy.php#extension
     - https://bitbucket.org/
     - https://content.dropboxapi.com/2/files/upload
     - https://github.com/openstyles/stylus
     - http://feross.org
     - https://userstyles.org/styles/
     - http://userscripts.com/
     - https://eslint.org/docs/rules/indent
     - https://eslint.org/docs/rules/require-unicode-regexp
     - http://socialfixer.com
     - http://contactbyweb.com/userscripts-mirror
     - https://github.com/zloirock/core-js/blob/v3.27.2/LICENSE
     - https://github.com/mysticatea
     - https://github.com/zloirock/core-js
     - http://www.w3.org/1999/xhtml
     - http://userscripts-mirror.org/scripts/review/
     - http://www.w3.org/1999/xlink
     - https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#
     - https://eslint.org/docs/rules/space-before-function-paren
     - https://github.com/eslint/doctrine
     - http://www.w3.org/2000/svg
     - http://tampermonkey.net/
     - https://www.tampermonkey.net/bug
     - https://opencollective.com/eslint
     - https://github.com/narcolepticinsomniac
     - https://www.tampermonkey.net/faq.php#Q204
     - https://www.instagram.com/der_jan_b/
     - http://userscripts-mirror.org/scripts/show/
     - https://www.googleapis.com/
     - https://github.com/estools/esrecurse
     - https://www.google.com/s2/favicons
     - http://json-schema.org/draft-04/schema#
     - https://blacklist.tampermonkey.net/get.php
     - https://www.fbpurity.com/
     - https://www.tampermonkey.net/faq.php#Q500
     - http://tmnk.net/faq#Q208
     - https://api.dropboxapi.com/2/files/list_folder
     - https://www.tampermonkey.net/privacy.php
     - https://www.tampermonkey.net/scripts.php
     - https://www.nczonline.net
     - https://eslint.org/docs/rules/no-multiple-empty-lines
     - https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.16.0/polyfill.js
     - https://www.tampermonkey.net/faq.php?
     - http://userscripts.org/
     - https://icons.duckduckgo.com/ip2/
     - https://github.com/estools/esrecurse.git
     - https://www.tampermonkey.net
     - http://json-schema.org/draft-07/schema
     - https://github.com/Constellation
     - https://gitlab.com/
     - https://vscode.dev/?connectTo=tampermonkey
     - https://api.onedrive.com/v1.0/drive/items/
     - https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.18.2/babel.js
     - http://github.com/garycourt/uri-js
     - https://github.com/derjanb
     - https://notify.dropboxapi.com/2/files/list_folder/longpoll
     - https://www.tampermonkey.net/documentation.php#_connect
     - https://sleazyfork.org/scripts/
     - https://www.tampermonkey.net/qr/
     - https://content.dropboxapi.com/2/files/download
     - https://www.tampermonkey.net/changelog.php?
     - https://openuserjs.org/scripts/
     - https://webdav.yandex.ru
     - https://stuk.github.io/jszip/documentation/howto/read_zip.html
     - https://www.tampermonkey.net/contrib.php?embedded=2
     - http://json-schema.org/schema
     - https://www.tampermonkey.net/installed.php?
     - https://www.tampermonkey.net/faq.php#Q207
    === GLOBAL RISK SCORE: 100/100 ===
    === PRIVACY RECOMMENDATION ===
    Always disable automatic updates for extensions when conducting security reviews. Updates may replace safe code with unsafe content without user confirmation.
    

    On ne peut pas mettre d'array dans le string...