feat: establish local-first scraping capability - #1
Conversation
Adversarial review — DO NOT SHIPComprehensive review of the full diff against the stated architecture, README claims, and release readiness for Verdict: DO NOT SHIP. The package does not import, several safety/limit invariants are missing or inverted, discovery is fused with acquisition, tests give false confidence, and multiple README/architecture promises are not implemented. Critical
High
Medium / other
Release blockers (must fix before
|
Re-review of head
|
| Prior issue | Status |
|---|---|
Broken from .models import |
Fixed (from ..models) |
| Robots unreachable → deny | Fixed (cache miss → allow) |
| Unbounded crawl downloads | Fixed (shared fetch_uri + _read_limited) |
| Discovery fused with acquisition | Fixed (discover_links returns URI list only) |
| Unbounded sitemap recursion | Fixed (depth / max_urls / max_total_size / DOCTYPE reject) |
| Duplicate content dropped URI provenance | Fixed (always append manifest) |
| CLI always exit 0 | Fixed (nonzero on failures) |
Package name scraping |
Fixed (flossware-scraping) |
Empty .gitignore |
Fixed |
| Thin tests / false confidence | Expanded (import, robots, size, redirect, sitemap, CLI, private IP, FTP mock) |
| README drift | Aligned with architecture and defaults |
Architecture (discovery → URI set → fetch_uri → AcquiredResource → corpus) now matches the README. SHA-256 identity + multi-URI manifest provenance is correct.
Remaining findings
Medium
-
Double-fetch on link discovery (
cli.py,discovery/web.py)- Discovery fetches every page to extract links; acquisition fetches the same URIs again.
- Correct separation; 2× bandwidth and rate-limit pressure on real hosts.
- Fix (optional for 0.1): document the cost clearly, or add a same-run transient body cache / optional acquire-during-discovery mode later.
-
DNS TOCTOU / residual SSRF (
acquisition/corpus.py)- Host is resolved and checked once; the later connect can hit a different address (rebinding).
- Fix: pin to allowed IPs, or document as accepted residual risk for a local CLI before calling the surface “SSRF-safe.”
-
getaddrinfohas no timeout (_blocked_address)- Hung DNS can stall the process indefinitely.
- Fix: bound DNS, or document dependency on system resolver timeouts.
-
crawl()compatibility alias drops security/limit knobs (discovery/web.py)- Alias does not forward
timeout,max_size,allow_private. - Fix: forward all kwargs, or remove the alias before public API freeze.
- Alias does not forward
-
Empty invocation succeeds (
cli.py)scrapewith no sources /--uris/--sitemapstores 0 and exits 0.- Fix: non-zero exit (or explicit warning) when zero targets were planned.
Low
- Premature empty
extracted//normalized/dirs (documented layout, no consumers yet). - Weak media-type detection (acceptable for acquisition-only scope if documented).
- No explicit max-redirects beyond urllib default.
- Tests are fully mocked; no live local HTTP server end-to-end test.
- FTP is “urllib under shared limits,” not deep FTP support — fine for 0.1 if not oversold.
Release readiness
| Gate | Status |
|---|---|
| Import / console script | OK |
| Size limits on all fetch paths | OK |
| Robots allow-when-unreachable | OK |
| Sitemap bounds | OK |
Private/link-local block + file:// redirect reject |
OK |
| CLI exit codes | OK |
| PyPI name | flossware-scraping |
| CI 3.10–3.14 | Green |
| Residual SSRF TOCTOU / DNS hang | Document or harden before claiming SSRF-safe |
| PyPI Trusted Publishing env | Must be configured in repo settings before first tag |
Recommended before tagging 0.1
- Document residual DNS-rebinding risk and DNS hang behavior, or harden the resolver path.
- Forward full kwargs from
crawlalias (or delete it). - Non-zero exit when no targets were provided.
- Document 2× fetch cost of discover-then-acquire.
- Confirm GitHub
pypienvironment + OIDC is configured before tagging0.1.
Nothing remaining looks like a hard correctness break for an Alpha 0.1 local-first CLI, provided the residual SSRF/DNS caveats are explicit. After those small fixes or documented acceptances, this is ready to merge and tag.
Summary
scrapingPython package andscrapeCLIBuild and release policy
Builds, tests, pull requests, and merges are tag-independent. The package version is read from
pyproject.toml; no Git tag is required for normal CI or artifact builds.The optional release workflow is manually invoked and publishes the immutable artifacts produced by that workflow. It does not gate ordinary builds or merges.
Notes
The implementation is stdlib-first and keeps parsing, normalization, chunking, embedding, indexing, and retrieval downstream of acquisition.