HTTrack Website Copier, copy websites to your computer (Official repository)
This report summarizes scanner evidence for xroche/httrack, a C project with 4,812 GitHub stars. SynthScan examined 229,616 lines of code across 587 source files, recording 277 pattern matches distributed across 12 syntactic categories. The legacy adjusted pattern signal is 2.0 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 230 finding(s).
Are imports declared and package/API names verified? 3 finding(s).
Are wrappers, guards, and repeated structures necessary? 58 finding(s).
Which changes need focused human review? 277 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 277 distinct pattern matches across 12 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/ftp-server.py | 63 | except OSError: | CODE |
| HIGH | tests/ftp-server.py | 117 | except OSError: | CODE |
| HIGH | tests/socks5-server.py | 185 | except OSError: | CODE |
| HIGH | tests/postfile-server.py | 56 | except (OSError, socket.timeout): | CODE |
| HIGH | tests/postfile-server.py | 68 | except OSError: | CODE |
| HIGH | tests/postfile-server.py | 64 | except OSError: | CODE |
| HIGH | tests/local-server.py | 4571 | except KeyboardInterrupt: | CODE |
| HIGH | tests/local-server.py | 1137 | except OSError: | CODE |
| HIGH | tests/local-server.py | 1198 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2408 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2462 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2481 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2503 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2559 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2579 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2609 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2635 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2715 | except OSError: | CODE |
| HIGH | tests/local-server.py | 3167 | except OSError: | CODE |
| HIGH | tests/local-server.py | 3201 | except OSError: | CODE |
| HIGH | tests/local-server.py | 3228 | except OSError: | CODE |
| HIGH | tests/local-server.py | 3297 | except OSError: | CODE |
| HIGH | tests/local-server.py | 3867 | except OSError: | CODE |
| HIGH | tests/local-server.py | 3897 | except OSError: | CODE |
| HIGH | tests/local-server.py | 1364 | except OSError: | CODE |
| HIGH | tests/local-server.py | 1972 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2066 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2251 | except OSError: | CODE |
| HIGH | tests/local-server.py | 1950 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2004 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2116 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2168 | except OSError: | CODE |
| HIGH | tests/local-server.py | 2211 | except OSError: | CODE |
| HIGH | tests/header-injection-server.py | 140 | except (OSError, socket.timeout): | CODE |
| HIGH | tests/header-injection-server.py | 173 | except OSError: | CODE |
| HIGH | tests/header-injection-server.py | 169 | except OSError: | CODE |
| HIGH | tests/status-line-server.py | 78 | except (OSError, socket.timeout): | CODE |
| HIGH | tests/proxytestlib.py | 43 | except OSError: | CODE |
| HIGH | tests/proxytestlib.py | 112 | except OSError: | CODE |
| HIGH | tests/proxytestlib.py | 49 | except OSError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | build.sh | 1 | #!/bin/sh | COMMENT |
| LOW | man/makeman.sh | 1 | #!/bin/sh | COMMENT |
| LOW | tools/tsan-suppressions.txt | 1 | # Races ThreadSanitizer reports that are deliberate, so the leg stays green and | COMMENT |
| LOW | tools/ci-sanitizer-report.sh | 1 | #!/bin/sh | COMMENT |
| LOW | tools/windows-kill-grade.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tools/parser-diff.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tools/buildroot-net.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tools/mkdeb.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | tools/mkdeb.sh | 21 | # -k, --key KEYID GPG key for signing (default: $DEBSIGN_KEYID) | COMMENT |
| LOW | tools/mk-sbuild-chroot.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | tools/downstream-drift.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tools/hurd-vm.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tools/hurd-vm.sh | 21 | VM_CPUS=1 | COMMENT |
| LOW | tests/crawllib.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tests/wsl2-exe-shim.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tests/check-test-names.sh | 41 | [ "$count" -gt 0 ] || bad "no test matched [0-9]*_*.test under $testdir" | COMMENT |
| LOW | tests/ci-windows-suite.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tests/ci-windows-suite.sh | 141 | # clock, but every outcome writes a line, the per-test timeout included, so $4 past | COMMENT |
| LOW | tests/ci-windows-suite.sh | 281 | # badmtime needs a filesystem that stores an mtime past gmtime's range; | COMMENT |
| LOW | tests/ci-windows-suite.sh | 301 | # which MSYS has no equivalent for, and this job sets no interposer's path; | COMMENT |
| LOW | tests/ci-windows-suite.sh | 381 | # process: 294's wizard never reaches its feof||ferror arm on a stdin that shell | COMMENT |
| LOW | tests/ci-windows-suite.sh | 501 | # Explicit now that this is a script; GitHub's "shell: bash" gave the step both. | COMMENT |
| LOW | tests/ci-windows-suite.sh | 541 | unset WATCHDOG_TOKEN | COMMENT |
| LOW | tests/ci-windows-suite.sh | 561 | TMPDIR="$(cygpath -m "$RUNNER_TEMP")" | COMMENT |
| LOW | tests/ci-windows-suite.sh | 581 | # both. Left at the serial budget though the pool costs 4 min, because it | COMMENT |
| LOW | tests/ci-suite-stage.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tests/local-crawl.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tests/local-crawl.sh | 21 | # --files-identical PATH PATH ... \ | COMMENT |
| LOW | tests/local-crawl.sh | 41 | # --rerun-args runs a second pass (same server and mirror dir) with the given | COMMENT |
| LOW | tests/altstackprobe.c | 21 | COMMENT | |
| LOW | tests/local-server.py | 561 | # record, so a test can drive the /sitemap.xml fallback instead. | COMMENT |
| LOW | tests/proclib.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tests/proclib.sh | 161 | } | COMMENT |
| LOW | tests/test-timeout.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tests/testlib.sh | 1 | #!/bin/bash | COMMENT |
| LOW | tests/testlib.sh | 421 | CLEANUP_FRAMES=() | COMMENT |
| LOW | tests/testlib.sh | 1161 | COMMENT | |
| LOW | tests/testlib.sh | 1301 | # reap a native Windows descendant -- the very case these watchdogs exist for -- | COMMENT |
| LOW | tests/testlib.sh | 1441 | # macOS and its signals can't reap httrack.exe on Windows. We poll and kill_tree. | COMMENT |
| LOW | tests/warc-validate.py | 1 | #!/usr/bin/env python3 | COMMENT |
| LOW | tests/warc-validate.py | 21 | # --same-resp-header SUBA=SUBB the response blocks targeting SUBA and SUBB | COMMENT |
| LOW | tests/arclib.sh | 1 | #!/bin/bash | COMMENT |
| LOW | html/fcguide.html | 261 | e go everywhere on the web | COMMENT |
| LOW | html/abuse.html | 461 | Example: (script & php, Unix)<br> | COMMENT |
| LOW | .github/workflows/windows-build.yml | 341 | key: wsl-rootfs-jammy-amd64-v1 | COMMENT |
| LOW | .github/workflows/ci.yml | 361 | # LSan only reports at a normal exit, so a test that kills the engine still | COMMENT |
| LOW | .github/workflows/ci.yml | 441 | # this to the allocation-failure self-tests: allocator_may_return_null lets | COMMENT |
| LOW | .github/workflows/ci.yml | 661 | # --unsigned: CI has no GPG key (also skips the release sig/checksums). | COMMENT |
| LOW | fuzz/run-fuzzers.sh | 1 | #!/bin/bash | COMMENT |
| LOW | src/htscore.c | 41 | /* File defs */ | COMMENT |
| LOW | src/htscore.c | 61 | COMMENT | |
| LOW | src/htsparse.h | 41 | #ifndef HTS_DEF_FWSTRUCT_htsblk | COMMENT |
| LOW | src/htscatchurl.c | 41 | #include "htsnet.h" | COMMENT |
| LOW | src/htscache.h | 41 | #include "htszlib.h" /* zipFile */ | COMMENT |
| LOW | src/htshelp.c | 41 | #include "htscoremain.h" | COMMENT |
| LOW | src/htshelp.c | 61 | #define HTS_PLATFORM_NAME "windows-x64" | COMMENT |
| LOW | src/htshelp.c | 161 | COMMENT | |
| LOW | src/htsbacktrace.c | 41 | COMMENT | |
| LOW | src/htsbacktrace.c | 61 | #if (defined(__linux) && defined(HAVE_BACKTRACE)) | COMMENT |
| LOW | src/htswin32.h | 41 | #ifdef _WIN32 | COMMENT |
| 103 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | man/makeman.sh | 17 | # makes it robust (no more prose turning into bogus options, see Debian #1061053): | COMMENT |
| MEDIUM | tools/emulated-suite.sh | 18 | # and the program one place right, which is what hid the engine from the harness | COMMENT |
| MEDIUM | tools/emulated-suite.sh | 21 | # same reason the harness sees the shift at all, and why a self-check here would | COMMENT |
| MEDIUM | tools/ci-sanitizer-report.sh | 84 | # Only the harness logs: an in-tree build puts the .test sources here too, and | COMMENT |
| MEDIUM | tools/parser-diff.sh | 174 | # TAP harness, so this waits longer than its 200 ticks suggest and strips the CR | COMMENT |
| MEDIUM | tools/hurd-vm.sh | 253 | # -j2, not the harness default: the VM has one processor and 2G, and at -j4 it | COMMENT |
| MEDIUM | tests/local-crawl.sh | 226 | # without routing the harness-read hts-log.txt through a non-ASCII path. | COMMENT |
| MEDIUM | tests/local-crawl.sh | 232 | # the logs (and the harness reads of them) go through the non-ASCII path | COMMENT |
| MEDIUM | tests/webhttracklib.sh | 229 | # A leaked htsserver wedges the parallel harness behind a green log. Bounded | COMMENT |
| MEDIUM | tests/local-server.py | 1959 | # Always-stall endpoint for 72_watchdog-crawl: never finishes, so the harness | COMMENT |
| MEDIUM | tests/proclib.sh | 153 | # harness script whose *path* contains "httrack" is not mistaken for the engine. | COMMENT |
| MEDIUM | tests/proclib.sh | 176 | # ptrace_scope=1, which is how a harness watchdog necessarily invokes it. | COMMENT |
| MEDIUM | tests/proclib.sh | 248 | # Report what a wedged test left behind, into the log the harness keeps: the test | COMMENT |
| MEDIUM | tests/testlib.sh | 19 | # Headed and indented, so a dumped log cannot be read as the harness's own | COMMENT |
| MEDIUM | tests/testlib.sh | 743 | # Which harness drives the tests: `msys` for an MSYS/Git Bash shell, `wsl2` for | COMMENT |
| MEDIUM | tests/testlib.sh | 811 | # is the harness's from here on; no test may hold it open across a poll. | COMMENT |
| MEDIUM | tests/testlib.sh | 864 | # trap, where a survivor would turn a passing test into a harness timeout. | COMMENT |
| MEDIUM | tests/testlib.sh | 1201 | # possibly the harness's own, and the taskkill above already reaped it. | COMMENT |
| MEDIUM | tests/install-headers-sweep.sh | 248 | # and an emulated compiler needs more time for it than the harness allows a test (#1146). | COMMENT |
| MEDIUM | fuzz/run-fuzzers.sh | 2 | # Drive every built harness against its seed corpus. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tools/check-includes.py | 72 | CODE | |
| LOW | tools/doc-links.py | 31 | CODE | |
| LOW | tools/gen-winprofile-keys.py | 31 | CODE | |
| LOW | tests/check-log-logins.py | 58 | CODE | |
| LOW | tests/check-log-logins.py | 73 | CODE | |
| LOW | tests/check-log-logins.py | 102 | CODE | |
| LOW | tests/png-colors.py | 24 | CODE | |
| LOW | tests/png-colors.py | 63 | CODE | |
| LOW | tests/ftp-server.py | 53 | CODE | |
| LOW | tests/ftp-server.py | 96 | CODE | |
| LOW | tests/ftp-server.py | 157 | CODE | |
| LOW | tests/ftp-server.py | 208 | CODE | |
| LOW | tests/check-subshell-errexit.py | 77 | CODE | |
| LOW | tests/local-server.py | 626 | CODE | |
| LOW | tests/local-server.py | 1931 | CODE | |
| LOW | tests/local-server.py | 1983 | CODE | |
| LOW | tests/local-server.py | 2094 | CODE | |
| LOW | tests/local-server.py | 2153 | CODE | |
| LOW | tests/local-server.py | 2196 | CODE | |
| LOW | tests/local-server.py | 3529 | CODE | |
| LOW | tests/local-server.py | 3758 | CODE | |
| LOW | tests/local-server.py | 4313 | CODE | |
| LOW | tests/local-server.py | 4449 | CODE | |
| LOW | tests/warc-validate.py | 143 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/testlib.sh | 634 | # Create the lock naming $1 as its owner, or fail because it exists. noclobber is | COMMENT |
| MEDIUM | html/div/search.sh | 11 | # Create an index if necessary | COMMENT |
| MEDIUM | src/htscookie_selftest.c | 1971 | ROW("# This file is generated by yt-dlp. Do not edit.\n", 0), | CODE |
| MEDIUM | src/htsbauth.c | 628 | "# This file is generated by yt-dlp. Do not edit.", | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/local-server.py | 1370 | def route_diskfull_splitindex(self): | CODE |
| LOW⚡ | tests/local-server.py | 1373 | def route_diskfull_splitsmallindex(self): | CODE |
| LOW⚡ | tests/local-server.py | 1376 | def route_diskfull_splitchunkedindex(self): | CODE |
| LOW⚡ | tests/local-server.py | 1384 | def route_diskfull_splitsmall(self): | CODE |
| LOW⚡ | tests/local-server.py | 1389 | def route_diskfull_splitchunked(self): | CODE |
| LOW | tests/local-server.py | 1306 | def route_diskfull_smallindex(self): | CODE |
| LOW | tests/local-server.py | 1325 | def route_diskfull_chunkedindex(self): | CODE |
| LOW | tests/local-server.py | 1346 | def route_diskfull_stallindex(self): | CODE |
| LOW | tests/local-server.py | 1421 | def route_diskfull_noclenindex(self): | CODE |
| LOW | tests/local-server.py | 1442 | def route_diskfull_gzbadindex(self): | CODE |
| LOW | tests/local-server.py | 2432 | def route_chunktrunc_alwaysbin(self): | CODE |
| LOW | tests/local-server.py | 3386 | def route_changes_redirtarget(self): | CODE |
| LOW | tests/local-server.py | 3416 | def route_changes_sizedtarget(self): | CODE |
| LOW | tests/local-server.py | 3429 | def route_changes_coded_stable(self): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | man/makeman.sh | 5 | # Usage: | COMMENT |
| LOW | tools/mkdeb.sh | 17 | # Usage: | COMMENT |
| LOW | tools/mk-sbuild-chroot.sh | 9 | # Usage: | COMMENT |
| LOW | tests/local-crawl.sh | 14 | # Usage: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/check-subshell-errexit.py | 127 | self.hit(left, "set -e is off inside a subshell left of && or ||") | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/wacz-validate.py | 81 | except Exception: | CODE |
| LOW | tests/wacz-validate.py | 85 | except Exception: | CODE |
| LOW | tests/local-crawl.sh | 135 | except Exception: | CODE |
| LOW | tests/warc-validate.py | 125 | except Exception as exc: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | html/fcguide.html | 112 | great job. Here's a simple example: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/wacz-validate.py | 80 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/htsparse.c | 860 | "lazy", "placeholder", "none", "null", "undefined", | CODE |