fix(assets): a remote reference is inert and a declared size is bounded
Two MAJOR findings of the independent v0.10.0 review, both with the shipped defaults, both new in 0.10.0. Repros rebuilt as tests first. - A remote <img src>/xlink:href became a LIVE markdown image link in the persisted concept, with the address and query string chosen by whoever wrote the document. Extraction opens no socket; a consumer rendering the bundle does. Now inert text with the address in a code span, pinned by a property over the readers rather than by one string. The tier asymmetry (user-upload refuses, trusted-source persisted) went to the guard repo with the repro. - Nothing bounded a declared image size: 9.6 KB of PDF declaring 3000x3000 grayscale zeros took 83 MB peak RSS, linear in pixels. MAX_IMAGE_PIXELS (40 000 000) and MAX_IMAGE_BYTES (256 MiB) are read off the corpora (largest measured 18.6 MP on K2, 1.4 MP on R761) and checked on what the container declares, before any decompression; over them is asset_too_large, counted. The same bound closes the inline data: URI, which the review flagged and did not measure. Also fixed, added by PM to this order: an inline PDF image was named from id() of a Python object, so two concept files of the reference corpus differed between builds. It is now named from its position. R761 unchanged: 50 carried of 50 found, assets diff -rq clean. Report: docs/2026-09-17-bildestien-0-10-1.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
864570b320
commit
230d1cbccd
8 changed files with 580 additions and 3 deletions
44
CHANGELOG.md
44
CHANGELOG.md
|
|
@ -68,6 +68,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
what the SOURCES declare. A refused document's pictures therefore no longer
|
||||
read as "0 of 0 found".
|
||||
|
||||
### Security
|
||||
|
||||
- **A remote image reference is no longer a live markdown image link
|
||||
(0.10.1).** New in 0.10.0: before it, no reader read an `<img>` attribute at
|
||||
all. A document could put ``
|
||||
into a persisted concept, with the address and query string chosen by
|
||||
whoever wrote the document. This package opens no socket, but a consumer
|
||||
that renders the bundle — or an agent that fetches what it renders — does,
|
||||
which turns "this bundle was opened" into a beacon, and a server-side
|
||||
consumer into an SSRF. The guard refuses such a line at
|
||||
`guard-user-upload` and the build's default tier does not, so the same bytes
|
||||
were persisted under the default and refused one tier up. A remote reference
|
||||
is now inert text with the address in a code span, and a property test over
|
||||
the readers asserts that no reference produces a markdown image link outside
|
||||
`assets/`. Found by an independent review of 0.10.0 before it was pushed.
|
||||
- **A declared image size is bounded (0.10.1).** Nothing limited a PDF image's
|
||||
size: a 9.6 KB file declaring 3 000 x 3 000 grayscale zeros took 83 MB of
|
||||
peak RSS and a 63 KB one declaring 8 000 x 8 000 took 276 MB, linear in the
|
||||
pixel count, so one document could exhaust memory and take a whole batch
|
||||
build with it — before any gate, because the guard never sees image bytes.
|
||||
`MAX_IMAGE_PIXELS` (40 000 000) and `MAX_IMAGE_BYTES` (256 MiB) are now
|
||||
checked against what the container DECLARES, before anything is
|
||||
decompressed, and a larger image is refused with `asset_too_large` and
|
||||
counted. The bound is read off the corpora: the largest image in the
|
||||
43-document reference corpus is 18.6 MP and R761's largest is 1.4 MP. The
|
||||
same bound closes the inline `data:` URI, which the review flagged as the
|
||||
same class and did not measure.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **An inline PDF image gets a stable name (0.10.1).** pdfminer names an
|
||||
inline image (`BI … EI`) from `id()` of a Python object, so a pointer line
|
||||
changed between two runs of one build and two concept files of the reference
|
||||
corpus differed — breaking the bit-exact rebuild invariant. Such an image is
|
||||
now named from its position on the page.
|
||||
|
||||
### Documented, not changed
|
||||
|
||||
- `images: N` in a concept counts POINTER BLOCKS, not unique pictures (12
|
||||
pointers to 2 files is `images: 12`). Now stated in the README.
|
||||
- A concept that is only a pointer block is persisted as substantive, because
|
||||
"degenerate" means zero characters after stripping whitespace and a pointer
|
||||
block is text.
|
||||
|
||||
## [0.10.0] — 2026-09-17
|
||||
|
||||
### Added
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue