← Files VeraARCHIVED FILE
modules/deep-research-validator/scripts/inspect_sources.py
20.8 KB · Oct 2, 2026 · 00:29 UTC
"""Inspect cited sources for an answer-validation run."""
from __future__ import annotations
import argparse
import hashlib
import http.client
import ipaddress
import json
import re
import socket
import ssl
import sys
import urllib.error
import urllib.parse
import urllib.request
from html.parser import HTMLParser
from pathlib import Path
from typing import Any
PLUGIN_ROOT = Path(__file__).resolve().parents[1]
for _vendor_root in (
PLUGIN_ROOT / "vendor" / "modules",
PLUGIN_ROOT.parent.parent / "vendor" / "modules",
PLUGIN_ROOT.parent / "_shared" / "vendor" / "modules",
):
if (_vendor_root / "vera_assurance").is_dir():
if str(_vendor_root) not in sys.path:
sys.path.insert(0, str(_vendor_root))
break
from vera_assurance import ( # noqa: E402
AssuranceContractError,
load_client_engagement_context_file,
)
__all__ = ["inspect_sources", "write_source_inventory"]
URL_RE = re.compile(r"https?://[^\s)\]>\"']+", re.IGNORECASE)
ACCESS_BARRIER_SNIPPETS = (
"access denied",
"forbidden",
"captcha",
"verify you are human",
"cloudflare",
"enable javascript",
"subscription",
"sign in",
"login",
)
_STANDARD_PORTS = {"http": 80, "https": 443}
MAX_CAPTURE_BYTES = 1_000_000
class UnsafePublicUrlError(ValueError):
"""Raised when a cited URL can reach a non-public network destination."""
def _validate_public_url(url: str) -> None:
"""Enforce public HTTP/S destinations for auditable SSRF prevention."""
parsed = urllib.parse.urlparse(url)
if (
parsed.scheme.lower() not in _STANDARD_PORTS
or not parsed.hostname
or parsed.username is not None
or parsed.password is not None
):
raise UnsafePublicUrlError("only public HTTP/S URLs without credentials")
try:
port = parsed.port
except ValueError as exc:
raise UnsafePublicUrlError("URL port is invalid") from exc
expected_port = _STANDARD_PORTS[parsed.scheme.lower()]
if port not in {None, expected_port}:
raise UnsafePublicUrlError("only standard HTTP/S ports are allowed")
hostname = parsed.hostname.rstrip(".").casefold()
if hostname == "localhost" or hostname.endswith(".localhost"):
raise UnsafePublicUrlError("localhost destinations are not allowed")
try:
literal = ipaddress.ip_address(hostname)
except ValueError:
literal = None
if literal is not None:
addresses = [literal]
else:
try:
addresses = [
ipaddress.ip_address(record[4][0])
for record in socket.getaddrinfo(
hostname,
expected_port,
type=socket.SOCK_STREAM,
)
]
except socket.gaierror:
raise
if not addresses or any(not address.is_global for address in addresses):
raise UnsafePublicUrlError("URL resolves to a non-public network address")
class _PublicRedirectHandler(urllib.request.HTTPRedirectHandler):
"""Reapply the public-network rule to every redirect target."""
def redirect_request(
self,
req: urllib.request.Request,
fp: Any,
code: int,
msg: str,
headers: Any,
newurl: str,
) -> urllib.request.Request | None:
_validate_public_url(newurl)
return super().redirect_request(req, fp, code, msg, headers, newurl)
def _connect_public_address(
address: tuple[str, int],
timeout: float | None,
source_address: tuple[str, int] | None = None,
) -> socket.socket:
"""Validate the exact socket destinations to prevent DNS rebinding."""
host, port = address
candidates = socket.getaddrinfo(host, port, type=socket.SOCK_STREAM)
if not candidates or any(
not ipaddress.ip_address(candidate[4][0]).is_global for candidate in candidates
):
raise UnsafePublicUrlError("Connection resolves to a non-public address")
last_error: OSError | None = None
for family, socktype, protocol, _, sockaddr in candidates:
connection = socket.socket(family, socktype, protocol)
try:
connection.settimeout(timeout)
if source_address is not None:
connection.bind(source_address)
connection.connect(sockaddr)
return connection
except OSError as exc:
connection.close()
last_error = exc
assert last_error is not None
raise last_error
class _PublicHTTPConnection(http.client.HTTPConnection):
"""HTTP connection whose socket cannot resolve unchecked destinations."""
def connect(self) -> None:
self._create_connection = _connect_public_address
super().connect()
class _PublicHTTPSConnection(http.client.HTTPSConnection):
"""HTTPS connection preserving TLS identity over a checked socket."""
def connect(self) -> None:
self._create_connection = _connect_public_address
# Keep the original hostname for TLS certificate validation and SNI.
super().connect()
class _PublicHTTPHandler(urllib.request.HTTPHandler):
"""Route HTTP requests through the public socket transport."""
def http_open(self, request: urllib.request.Request) -> Any:
return self.do_open(_PublicHTTPConnection, request)
class _PublicHTTPSHandler(urllib.request.HTTPSHandler):
"""Route HTTPS requests through the public socket transport."""
_context: ssl.SSLContext | None
def https_open(self, request: urllib.request.Request) -> Any:
return self.do_open(_PublicHTTPSConnection, request, context=self._context)
def _ordered_unique(items: list[str]) -> list[str]:
seen: set[str] = set()
out: list[str] = []
for item in items:
cleaned = item.strip().rstrip(".,;:!?)\\]}>'\"")
if not cleaned:
continue
# URL paths and query values can be case-sensitive.
key = cleaned
if key in seen:
continue
seen.add(key)
out.append(cleaned)
return out
def _read_json(path: Path) -> dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8"))
def _extract_urls_from_inventory(path: Path) -> list[str]:
payload = _read_json(path)
urls = [str(url) for url in payload.get("urls", [])]
for footnote in payload.get("footnotes", []):
urls.extend(URL_RE.findall(str(footnote.get("text", ""))))
for link in payload.get("markdown_links", []):
urls.append(str(link.get("url", "")))
return _ordered_unique(urls)
def _source_file_record(
path: Path,
*,
run_root: Path | None = None,
) -> dict[str, Any]:
resolved = path.expanduser().resolve()
recorded_path = resolved.as_posix()
path_reference = "absolute"
if run_root is not None:
try:
recorded_path = resolved.relative_to(run_root.resolve()).as_posix()
except ValueError as exc:
raise ValueError(
"managed source file is outside the current customer run"
) from exc
path_reference = "run_root_relative"
try:
raw = resolved.read_bytes()
except OSError as exc:
return {
"kind": "file",
"name": path.name,
"path_reference": path_reference,
"path": recorded_path,
"origin_path": recorded_path,
"status": "unreadable",
"character_count": 0,
"capture_scope": "unavailable_local_source",
"error": type(exc).__name__,
"excerpt": "",
}
text, extraction = _extract_source_text(raw, suffix=path.suffix)
encoded = text.encode("utf-8")
return {
"kind": "file",
"name": path.name,
"path_reference": path_reference,
"path": recorded_path,
"origin_path": recorded_path,
"status": extraction["status"],
"character_count": len(text.strip()),
"content_hash": hashlib.sha256(encoded).hexdigest(),
"source_bytes_sha256": hashlib.sha256(raw).hexdigest(),
"extracted_text_sha256": hashlib.sha256(encoded).hexdigest(),
"capture_scope": (
"complete_local_text"
if extraction["format"] == "text" and text
else extraction["capture_scope"]
),
"extraction": extraction,
"excerpt": re.sub(r"\s+", " ", text.strip())[:1200],
"_captured_text": text,
}
def _looks_blocked(text: str) -> bool:
"""Flag exact interstitial text as a hint, never a semantic access verdict."""
sample = re.sub(r"\s+", " ", text).strip().casefold()
return sample in ACCESS_BARRIER_SNIPPETS
class _HTMLTextExtractor(HTMLParser):
"""Parse HTML syntax so quoted attributes cannot enter source evidence."""
def __init__(self) -> None:
super().__init__(convert_charrefs=True)
self.parts: list[str] = []
self.hidden_tag: str | None = None
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
if tag in {"script", "style"}:
self.hidden_tag = tag
self.parts.append(" ")
def handle_endtag(self, tag: str) -> None:
if tag == self.hidden_tag:
self.hidden_tag = None
self.parts.append(" ")
def handle_data(self, data: str) -> None:
if self.hidden_tag is None:
self.parts.append(data)
def _extract_source_text(
raw: bytes, *, content_type: str = "", suffix: str = ""
) -> tuple[str, dict[str, Any]]:
"""Decode formats mechanically and disclose uncovered pages or failures."""
is_pdf = (
raw.startswith(b"%PDF-")
or content_type == "application/pdf"
or suffix.lower() == ".pdf"
)
if is_pdf:
import fitz
try:
with fitz.open(stream=raw, filetype="pdf") as document:
pages = [page.get_text() for page in document]
except (fitz.FileDataError, RuntimeError, ValueError) as exc:
return "", {
"format": "pdf",
"status": "unreadable",
"capture_scope": "extraction_failed",
"error": str(exc),
}
empty_pages = [
index for index, page in enumerate(pages, start=1) if not page.strip()
]
text = "\n\n".join(
f"[Page {index}]\n{page}"
for index, page in enumerate(pages, start=1)
if page.strip()
)
complete = bool(pages) and not empty_pages
return text, {
"format": "pdf",
"status": (
"available" if complete else "partial_text" if text else "unreadable"
),
"capture_scope": (
"all_pages_extracted_text" if complete else "incomplete_page_text"
),
"page_count": len(pages),
"pages_without_text": empty_pages,
}
try:
text = raw.decode("utf-8-sig")
except UnicodeDecodeError:
return "", {
"format": "unknown",
"status": "unreadable",
"capture_scope": "unsupported_encoding",
}
is_html = (
content_type == "text/html"
or suffix.lower() in {".html", ".htm"}
or bool(re.match(r"\s*(?:<!doctype html|<html|<body)", text, re.I))
)
if is_html:
parser = _HTMLTextExtractor()
parser.feed(text)
parser.close()
text = "".join(parser.parts)
elif b"\x00" in raw or (
content_type
and not content_type.startswith("text/")
and content_type not in {"application/json", "application/xml"}
):
return "", {
"format": "unknown",
"status": "unreadable",
"capture_scope": "unsupported_format",
}
return text, {
"format": "html" if is_html else "text",
"status": "available" if text.strip() else "empty",
"capture_scope": "complete_extracted_text",
}
def _fetch_url_record(url: str, timeout: float) -> dict[str, Any]:
try:
_validate_public_url(url)
except UnsafePublicUrlError as exc:
return {
"kind": "url",
"url": url,
"status": "blocked_non_public_destination",
"http_status": 0,
"character_count": 0,
"excerpt": "",
"error": str(exc),
}
except socket.gaierror as exc:
return {
"kind": "url",
"url": url,
"status": "unreachable",
"http_status": 0,
"character_count": 0,
"excerpt": "",
"error": str(exc),
}
request = urllib.request.Request(
url,
headers={"User-Agent": "MparanzaDeepResearchValidator/0.1"},
)
opener = urllib.request.build_opener(
_PublicRedirectHandler(),
urllib.request.ProxyHandler({}),
_PublicHTTPHandler(),
_PublicHTTPSHandler(),
)
try:
with opener.open(request, timeout=timeout) as response:
raw = response.read(MAX_CAPTURE_BYTES + 1)
truncated = len(raw) > MAX_CAPTURE_BYTES
raw = raw[:MAX_CAPTURE_BYTES]
status = int(getattr(response, "status", 0) or 0)
geturl = getattr(response, "geturl", None)
final_url = str(geturl()) if callable(geturl) else url
headers = getattr(response, "headers", None)
declared_length = str(headers.get("Content-Length", "")) if headers else ""
incomplete_response = declared_length.isdecimal() and int(
declared_length
) > len(raw)
content_type = (
str(headers.get_content_type())
if headers is not None and hasattr(headers, "get_content_type")
else ""
)
except UnsafePublicUrlError as exc:
return {
"kind": "url",
"url": url,
"status": "blocked_non_public_destination",
"http_status": 0,
"character_count": 0,
"excerpt": "",
"error": str(exc),
}
except urllib.error.HTTPError as exc:
return {
"kind": "url",
"url": url,
"status": "http_error",
"http_status": int(exc.code),
"character_count": 0,
"excerpt": "",
"error": str(exc),
}
except (urllib.error.URLError, TimeoutError, OSError) as exc:
return {
"kind": "url",
"url": url,
"status": "unreachable",
"http_status": 0,
"character_count": 0,
"excerpt": "",
"error": str(exc),
}
cleaned, extraction = _extract_source_text(raw, content_type=content_type)
cleaned = re.sub(r"\s+", " ", cleaned).strip()
# HTTP 206 mechanically identifies a partial representation, even when
# every byte of this particular response was captured.
partial_response = status == 206
parse_status = (
"partial_capture"
if truncated or partial_response or incomplete_response
else extraction["status"]
)
return {
"kind": "url",
"url": url,
"requested_url": url,
"final_url": final_url,
"status": parse_status,
"http_status": status,
"character_count": len(cleaned),
"content_type": content_type,
"content_hash": hashlib.sha256(raw).hexdigest(),
"source_bytes_sha256": hashlib.sha256(raw).hexdigest(),
"extracted_text_sha256": hashlib.sha256(cleaned.encode("utf-8")).hexdigest(),
"extraction": extraction,
"access_barrier_hint": _looks_blocked(cleaned),
"capture_scope": (
f"first_{MAX_CAPTURE_BYTES}_response_bytes"
if truncated
else (
"incomplete_response"
if incomplete_response
else "partial_response" if partial_response else "complete_response"
)
),
"capture_truncated": truncated,
"excerpt": cleaned[:1200],
"error": "",
"_captured_text": cleaned,
}
def _persist_source_captures(
records: list[dict[str, Any]],
*,
capture_dir: Path | None,
capture_base_dir: Path | None,
) -> list[dict[str, Any]]:
"""Assign stable IDs and optionally persist complete normalized snapshots."""
if capture_dir is not None:
capture_dir.mkdir(parents=True, exist_ok=True)
selected_base = capture_base_dir or capture_dir
base_dir = selected_base.resolve() if selected_base is not None else None
out: list[dict[str, Any]] = []
canonical_captures: dict[str, tuple[str, str]] = {}
for index, raw_record in enumerate(records, start=1):
record = dict(raw_record)
source_id = f"source-{index:03d}"
record["source_id"] = source_id
captured_text = str(record.pop("_captured_text", "") or "")
if capture_dir is not None and base_dir is not None and captured_text:
content_hash = str(record.get("content_hash") or "")
canonical = canonical_captures.get(content_hash) if content_hash else None
if canonical is None:
capture_path = capture_dir / f"{source_id}.txt"
capture_path.write_text(captured_text, encoding="utf-8")
captured_text_path = (
capture_path.resolve().relative_to(base_dir).as_posix()
)
record["captured_text_path"] = captured_text_path
if content_hash:
canonical_captures[content_hash] = (source_id, captured_text_path)
else:
canonical_source_id, captured_text_path = canonical
record["captured_text_path"] = captured_text_path
record["duplicate_content_of_source_id"] = canonical_source_id
record["capture_reused"] = True
out.append(record)
return out
def inspect_sources(
inventory_path: Path,
*,
source_files: list[Path] | None = None,
timeout: float = 10.0,
fetch_urls: bool = True,
capture_dir: Path | None = None,
capture_base_dir: Path | None = None,
run_root: Path | None = None,
) -> dict[str, Any]:
"""Return deterministic source inventory."""
urls = _extract_urls_from_inventory(inventory_path)
url_records = (
[_fetch_url_record(url, timeout) for url in urls]
if fetch_urls
else [
{
"kind": "url",
"url": url,
"status": "listed_not_fetched",
"http_status": 0,
"character_count": 0,
"excerpt": "",
"error": "",
}
for url in urls
]
)
file_records = [
_source_file_record(path, run_root=run_root) for path in (source_files or [])
]
records = _persist_source_captures(
[*url_records, *file_records],
capture_dir=capture_dir,
capture_base_dir=capture_base_dir,
)
return {
"path_reference": ("run_root_relative" if run_root is not None else "absolute"),
"url_count": len(urls),
"file_count": len(file_records),
"sources": records,
}
def write_source_inventory(
inventory_path: Path,
output_dir: Path,
*,
source_files: list[Path] | None = None,
timeout: float = 10.0,
fetch_urls: bool = True,
run_root: Path | None = None,
) -> dict[str, Path]:
output_dir.mkdir(parents=True, exist_ok=True)
payload = inspect_sources(
inventory_path,
source_files=source_files,
timeout=timeout,
fetch_urls=fetch_urls,
capture_dir=output_dir / "sources",
capture_base_dir=output_dir,
run_root=run_root,
)
path = output_dir / "source_inventory.json"
path.write_text(
json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
)
return {"source_inventory": path}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("document_inventory", type=Path)
parser.add_argument("--output-dir", type=Path, required=True)
parser.add_argument("--client-engagement", type=Path, required=True)
parser.add_argument("--source-file", type=Path, action="append", default=[])
parser.add_argument("--timeout", type=float, default=10.0)
parser.add_argument("--no-fetch", action="store_true")
args = parser.parse_args()
try:
context = load_client_engagement_context_file(
args.client_engagement,
expected_workflow_id="deep-research-validator",
input_paths=[args.document_inventory, *args.source_file],
output_dir=args.output_dir,
)
except AssuranceContractError as exc:
parser.error(str(exc))
write_source_inventory(
args.document_inventory,
args.output_dir,
source_files=args.source_file,
timeout=args.timeout,
fetch_urls=not args.no_fetch,
run_root=Path(context["run_root"]),
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
SHA-256: e257e2ce924c6b9b74d724bcb1b584301d6ab02f98a2a6c1986135a3c8f1ba7f