Skip to content

Exit 5 for content never obtained, exit 2 for usage errors, CSV formula guard, umask-respecting outputs - #4

Merged
jehrr merged 3 commits into
mainfrom
fix/audit-2026-10-14
Oct 15, 2026
Merged

jehrr merged 3 commits into
mainfrom
fix/audit-2026-10-14

Conversation

@jehrr

@jehrr jehrr commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes from the 2026-10-14 audit of main @ 5426c9f (v1.0.2). Every fix has a planted-fault control that turns the offline suite red on the named check.

Behaviour changes (lead the next release notes)

  • Zero rows → exit 5 unless the listing was read to its end. finish_run returned 4 for every unblocked zero-row run; a probe gave page_load_timeout/pages_failed/no_profile/transport_error → 4, and a fake-driver item run on a page with no item exited 4. Now only completed / pagination_exhausted / no_new_products / single_page_mode keep 4; any other reason (including one added later) is 5. Blocked stays 3.
  • Usage errors → exit 2. Wrong host, robots-disallowed URL, wrong --mode, two URLs outside item mode and an unreadable --from were SystemExit("msg") (exit 1; --from missing was a traceback). Now checked in parse_args via parser.error, and run() raises a SystemExit subclass with code 2.
  • Output modes. Outputs were 0600 (mkstemp + replace). New files now get 0o666 & ~umask; an existing file keeps its mode (rows, sidecar, attempt sidecar).
  • One paid solve per page (F6). A page that stayed walled bought one solve per exit: with the solver stubbed, 2 solves at the default --proxy-block-retries 1, 4 at 3. solve_wall (the only purchase point; all three engines share it) now draws from a per-URL allowance page_flow.SOLVES_PER_PAGE = 1, checked BEFORE buying, not reset by retries or rotation. Refusals are counted as transport_facts.solves_refused_by_budget. Same probe now: 1 solve, rotations still happen, exit 3. Note: the README's measured case where a fresh exit's second token was accepted would now end blocked; that is the family rule.
  • Selenium + Scraping Browser. AVITO_CDP_ENDPOINT from .env made every Selenium run exit 5 transport_error. A typed --cdp-endpoint is now exit 2; one from the environment is ignored with a notice.

Other fixes

  • CSV formula guard: string cells starting = + - @ TAB CR LF (after list/dict joining) get a leading ' in CSV only; numbers untouched; JSON unchanged; csv_cells_escaped in the sidecar (caller extra stays under transport_facts, not overwritten).
  • tools/scan_secrets.py --worktree: also reads untracked non-ignored files, skips dirs holding pyvenv.cfg, prints the file count, fails when it read 0 files (was "clean" with no .git).
  • pyppeteer CDP connect bounded (30 s asyncio.wait_for).
  • Private section references (§10, §21 in 4 files) and sibling-repo pointers in comments removed; new suite guard fails on such citations (tokens assembled so the guard does not match itself). Release notes v1.0.0–v1.0.2 checked: none contained citations.
  • requirements-playwright.txt → playwright>=1.44 (matches the extra); .gitignore covers *_p[0-9]*.txt/.htm dumps.
  • Repo description: "the 2Captcha Scraping Browser API" (was "the Scraping Browser API").

Canary (owner request 2026-10-15)

  • Before: no secrets or variables are set, so all 9 runs from 2026-10-06 to 2026-10-14 took the green SKIP branch and never touched avito.ru.
  • After: a missing TWOCAPTCHA_KEY, AVITO_PROXY or AVITO_BROWSER_ACCOUNT_ID fails the job with ::error:: naming each one. A live run that exits anything but 0 (3 blocked, 4/5, 6) is red. The data check no longer asks for a ≥50% seller-name share, which depends on the category's merchandising; it requires sku, url and title on every row, alongside the existing checks (≥100 rows, complete, ≥3 pages, ≥90% priced, region_slug, unique sku and page+position).
  • There is no bare-runner path: the README and the measurements show a datacentre or non-Russian address is refused, so the Scraping Browser path stays.
  • Dispatched on this branch: https://github.com/2scraper/avito-scraper/actions/runs/38694474556 is red as expected, naming all three missing credentials. It stays red until the owner adds them.
  • Suite guard test_canary_cannot_go_green_without_scraping, with 3 controls, each RED on its named check: an error downgraded to a warning, the rc gate disabled, the sku/url/title loop emptied.

Evidence

  • Core-only venv (requirements.txt): smoke_test.py → 576 checks (after the budget commit; 570 before), 0 failed, 6 skipped (engines absent). Per engine venv (no browser download): selenium 587/0 failed, pyppeteer 585/0, playwright 587/0, each skipping only the absent engines.
  • ci_checks.py --help-check / --secret-check / --sample-check ok.
  • Controls (PYTHONDONTWRITEBYTECODE=1, __pycache__ removed per run), all RED on the named check, green after restore: exit-5 rule removed; chmod removed; existing-mode read removed; CSV apostrophe removed; sidecar count removed; UsageError code reverted to message; parse_args URL check removed; env endpoint not dropped; typed endpoint not refused; untracked files not scanned; venv skip removed; zero-file guard removed; a §10 re-planted in a comment; budget check disabled, _spend_solve call removed, allowance not kept in facts (all three RED on "one paid solve for one page", got 4 want 1).

Not changed (and why)

  • hCaptcha wording in README/TROUBLESHOOTING/CONTRIBUTING/CHANGELOG/release notes — owner decision: leave as is.
  • --proxy-rotate per-page implement-or-remove (F7), fingerprints claim / unused fingerprint_client.py (F8; "fingerprints" left in the description) — marked owner decisions in the audit.
  • pyppeteer page.authenticate for a local authenticated proxy (F10) — needs a live check with a proxy; only the connect timeout was added.
  • A dedicated not_found state/exit for HTTP 404 items — open question; it now exits 5 under the general rule.
  • Policy-table columns with no runtime reader (F14, low), version bump/tag/release.

🤖 Generated with Claude Code

jehrr and others added 3 commits October 14, 2026 18:55
…la guard, umask-respecting outputs

- finish_run: zero rows exit 4 only for stop reasons meaning the listing was
  read to its end; any other reason (failed page, item with no item, profile
  never parsed, a reason added later) exits 5.
- URL/host/robots/--mode/--from refusals are argparse errors (exit 2), checked
  in parse_args before a browser starts; run() raises a SystemExit(2) subclass.
- Atomic writer: new files 0o666 & ~umask, existing files keep their mode.
- CSV: text cells starting = + - @ TAB CR LF get a leading apostrophe after
  list/dict serialisation; csv_cells_escaped recorded in the sidecar.
- Selenium: typed --cdp-endpoint exits 2; one from .env is ignored with a notice.
- scan_secrets --worktree reads untracked, non-ignored files, skips pyvenv.cfg
  directories, fails when it read nothing.
- pyppeteer CDP connect bounded at 30 s.
- Private section references and sibling-repo pointers removed; suite guard
  added. requirements-playwright aligned with the extra; .gitignore covers
  non-.html dump names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tions

A page that stayed walled bought one solve per exit (2 by default, N+1 with
--proxy-block-retries N). solve_wall, the only purchase point shared by all
three engines, now draws from a per-URL allowance (page_flow.SOLVES_PER_PAGE)
checked before buying; rotation re-fetches the page without resetting it.
Refusals are counted in transport_facts.solves_refused_by_budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Missing TWOCAPTCHA_KEY, AVITO_PROXY or AVITO_BROWSER_ACCOUNT_ID now fails the
job with an error naming each one; a live run that does not exit 0 is red.
The data check requires sku, url and title on every row instead of a
seller-name share, which depends on the category's private/company mix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jehrr
jehrr marked this pull request as ready for review October 15, 2026 12:49
@jehrr
jehrr merged commit 72835e5 into main Oct 15, 2026
7 of 8 checks passed
@jehrr
jehrr deleted the fix/audit-2026-10-14 branch October 15, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant