hirara-core
Shared building blocks for Hirara agent tools. Small on purpose.
Why it exists
A tool that fetches an attacker-influenceable URL needs an SSRF perimeter. If
each tool hand-rolls its own, a new tool eventually ships without one — and
that is not hypothetical: the transcription tool added a URL-fetch path with a
bare httpx.get(url, follow_redirects=True) and no guard at all. This package
is the one guard every tool imports, so that hole can only be fixed — or
forgotten — in one place.
What's in it
| Symbol | Role |
|---|---|
resolve_target(url) |
Resolve-then-pin: validates the URL, checks every returned address, returns a Target pinned to one vetted IP. Raises BlockedURL. |
check_ip(ip) |
Is this address globally routable and not reserved? |
safe_download(url) |
A full redirect-safe, size-capped fetcher built on the guard — re-validates every hop, pins each request, streams and aborts at max_bytes. |
Usage
from hirara_core import safe_download, BlockedURL, DownloadError
try:
result = await safe_download(audio_url, max_bytes=25 * 1024 * 1024)
data = result.content # bytes, never larger than max_bytes
except BlockedURL as exc:
... # the URL is not allowed — a private IP, a bad scheme, a redirect inward
except DownloadError as exc:
... # it was allowed but did not work — bad status, transport error
BlockedURL (a security refusal) is kept distinct from DownloadError (an
ordinary failure) on purpose — a caller should treat "not allowed" and "did
not work" differently.
The two rules
- Resolve, then check every address, then pin. Rejecting only the first resolved address lets a resolver hand back one good IP and one bad one.
- Connect to the pinned IP, hostname in
Host+ SNI. Validating a name and letting the client resolve it again is a DNS-rebinding hole.
safe_download runs every redirect hop back through rule 1, so a public URL
that 302s to http://169.254.169.254/ is rejected at the hop.
Tests
pip install -e '.[dev]'
pytest -q
One case per real technique: metadata IPs, CGNAT, v4-mapped IPv6, 6to4, split-horizon DNS answers, credential-stuffed URLs, non-web ports, redirect-to- metadata, and the streaming byte cap.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hirara_core-0.1.0.tar.gz.
File metadata
- Download URL: hirara_core-0.1.0.tar.gz
- Upload date:
- Size: 9.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
518135fb6f45b5f823d0d95b9e39405d9d9c9c28a1918f0e8a5bbb070cbd5cbb
|
|
| MD5 |
209f7c6fc1895e35e20f589e9c89dcc4
|
|
| BLAKE2b-256 |
e0be3c5935839b88c47cfd5a4bf378b8547938d94291979317fb0a34e084205f
|
Provenance
The following attestation bundles were made for hirara_core-0.1.0.tar.gz:
Publisher:
pypi-publish.yml on lucasdmarshall/Hirara
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hirara_core-0.1.0.tar.gz -
Subject digest:
518135fb6f45b5f823d0d95b9e39405d9d9c9c28a1918f0e8a5bbb070cbd5cbb - Sigstore transparency entry: 2361780263
- Sigstore integration time:
-
Permalink:
lucasdmarshall/Hirara@a2dce6ceff7db8527a994bec2286597464bec7f9 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/lucasdmarshall
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@a2dce6ceff7db8527a994bec2286597464bec7f9 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file hirara_core-0.1.0-py3-none-any.whl.
File metadata
- Download URL: hirara_core-0.1.0-py3-none-any.whl
- Upload date:
- Size: 7.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0aa0ad241861ccadb66b07e03b0549224e1a65dc908881b0e04fbff54131867
|
|
| MD5 |
82fae8118d270d916d7d7c970c780983
|
|
| BLAKE2b-256 |
37d1a2f123491716059cdaa3188cf72a5335ce2eeefb5d3f6932addd729b1db0
|
Provenance
The following attestation bundles were made for hirara_core-0.1.0-py3-none-any.whl:
Publisher:
pypi-publish.yml on lucasdmarshall/Hirara
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hirara_core-0.1.0-py3-none-any.whl -
Subject digest:
f0aa0ad241861ccadb66b07e03b0549224e1a65dc908881b0e04fbff54131867 - Sigstore transparency entry: 2361780268
- Sigstore integration time:
-
Permalink:
lucasdmarshall/Hirara@a2dce6ceff7db8527a994bec2286597464bec7f9 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/lucasdmarshall
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@a2dce6ceff7db8527a994bec2286597464bec7f9 -
Trigger Event:
workflow_dispatch
-
Statement type: