T08 · Insecure Dependencies
- Location
scripts/fetch_dict.py:55- Finding
Downloaded Dictionary Database Is Installed Without Integrity Verification
- Content
View full analysis
Vulnerability Details
File Location:
scripts/fetch_dict.py, lines 55–60 and 96–102
Vulnerability Type: Supply-chain integrity failure
Risk Level: MediumVulnerable Code
python return { "tag": tag, "published": published, "download_url": asset["browser_download_url"], "size": asset["size"], "digest": asset.get("digest", ""), }python # Verify size actual_size = os.path.getsize(dest_path + ".tmp") if actual_size != expected_size: print(f"WARNING: Downloaded size ({actual_size}) != expected ({expected_size})", file=sys.stderr) # Atomic rename os.replace(dest_path + ".tmp", dest_path) print(f"Saved to: {dest_path}")Technical Analysis
The downloader obtains a digest from the GitHub release metadata but never uses it to authenticate the downloaded database. It only compares the downloaded file size with the size reported by the same remote metadata source.
File size is not a cryptographic integrity control. An attacker able to replace the release asset or control the corresponding release metadata can provide a malicious file with the expected size. Furthermore, even when the size differs, the script only prints a warning and still replaces the destination database.
HTTPS protects the connection in transit under normal conditions, but it does not protect against compromise of the upstream repository, release account, or published asset. Because the release is dynamically selected through the
latestendpoint, the effective database content can change after this project has been reviewed.Attack Path
- An attacker compromises the upstream release process, repository account, or release asset.
- The attacker publishes or replaces
Dict-Sqlite.dbwith manipulated translation records. - A user runs
python3 scripts/fetch_dict.py. - The script retrieves the attacker-controlled asset URL and downloads the database.
- No cryp ...[truncated 1132 chars]
- Remediation
View remediation
Remediation Suggestions
-
Cryptographically verify the downloaded asset before installation. Parse the release-provided SHA-256 digest and compare it using a constant-time comparison:
python import hashlib import hmac def sha256_file(path): digest = hashlib.sha256() with open(path, "rb") as source: for chunk in iter(lambda: source.read(1024 * 1024), b""): digest.update(chunk) return digest.hexdigest() expected_digest = info["digest"] if not expected_digest.startswith("sha256:"): raise RuntimeError("A valid SHA-256 release digest is required") expected_hash = expected_digest.split(":", 1)[1].lower() actual_hash = sha256_file(dest + ".tmp") if not hmac.compare_digest(actual_hash, expected_hash): os.remove(dest + ".tmp") raise RuntimeError("Downloaded database failed SHA-256 verification") -
Treat a size mismatch as a fatal error. Delete the temporary file and preserve the existing database rather than continuing to
os.replace(). -
Require a digest instead of accepting an empty value. If trustworthy release metadata does not provide one, distribute a separately authenticated checksum or signature.
-
For stronger reproducibility, pin a reviewed release tag and expected digest rather than automatically trusting the latest mutable release.
-
Validate the SQLite file before installation, including its file header and expected schema. Open it in read-only mode and confirm that required tables and columns are present.
-
Place verification and validation before the atomic replacement so any failure leaves the previously trusted database intact.
-
