You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Docbank now runs the existing Mistral OCR path through the same provider-neutral rendition contract as Docling and Datalab. It verifies one sealed upload, resolves the named credential per attempt, keeps authority expiry active through upload and retries, and converts Mistral pages into complete PDF evidence plus bounded Markdown and a sanitized receipt.
Docbank counts PDF pages locally before egress. Oversized documents, missing returned pages, malformed output, filtered or compressed authority it cannot inspect, and unsupported non-PDF formats fail closed.
Why
Mistral should not bypass the exact source, disclosure, output, and evidence rules just because an OCR client already existed. Provider support also cannot be broader than Docbank’s enforceable local unit authority.
Usage
For library use, construct the adapter with mistral.NewRenditionProvider(profile, secrets, httpClient) and pass it an authorized PDF. PDF is the only advertised format in this PR; non-PDF input remains blocked until its local unit bounds are enforceable.
Daemon, API, and CLI registration is intentionally deferred to S1–S3.
Code review found 2 High and 2 Medium issues requiring attention.
High
Ordinary GC can strand sensitive derivative bytes in immutable packs — internal/store/gc.go:1293
Paged GC treats derivative_blob_purge_pending hashes as eligible. If derivative purge crashes after recording the logical purge but before RecordDerivativePackPurgeTargets, ordinary GC can delete the blob row and cascade-delete the pending hash, leaving no receipt through which a retry can retire the packed data. Fix: Exclude pending derivative hashes from ordinary GC, or atomically record exact packed locations before deleting blob rows. Add coverage for crash → ordinary GC → purge retry with a packed derivative.
Bridge downloads artifacts before enforcing authorization limits — document/bridge/client.go:390
Completed artifacts are fetched before validating count, roles, or cumulative size. Arbitrarily many distinct invalid roles bypass duplicate-role checks, allowing a malicious bridge to trigger repeated downloads and retain large payloads before final validation rejects them. Fix: Preflight artifact count, allowed roles, and declared cumulative bytes before downloading. Enforce the remaining authorization budget on every response body and artifact fetch.
Medium
Bridge operations can continue after authorization expiry — document/bridge/client.go:124 Render checks expiration only initially, while its context is bounded solely by totalTimeout. Document upload, polling, and artifact retrieval can therefore continue after authorization.ExpiresAt; provider-supplied receipt timestamps may conceal the violation. Fix: Bound the entire operation by the earliest of authorization expiry, caller deadline, and configured timeout. Recheck expiration immediately before each outbound request.
Oversized list metadata can cause indefinite processing retries — internal/processing/source_metadata.go:174 metadataCollector.strings does not enforce MaxSourceMetadataValueBytes per entry, although the codec does. Parseable documents containing an oversized list value can produce metadata that always fails marshaling, preventing generation publication. Fix: Apply codec-compatible bounds during collection, omit oversized values with a warning, and test extraction followed by successful marshaling.
Code review found one High-severity security issue and four Medium-severity authorization/resource-limit issues.
High
document/upload/spool_windows.go:29 — The Windows spool directory relies on Unix permission bits, which Windows ignores, and may inherit a DACL allowing other local accounts to read uploaded source documents. Create it with winsecurity.MkdirPrivatePinnedAt, verify its creation identity and protected current-user DACL, and write no bytes before verification succeeds.
Medium
document/bridge/client.go:124 — Bridge operations do not honor authorization.ExpiresAt, allowing external requests after authorization expires. Set the deadline to the earliest caller deadline, total timeout, or authorization expiry, and check expiry before every outbound request.
document/bridge/client.go:261 — Uploaded bytes are checked only for length, not against the authorized SHA-256 digest. Stage and verify the exact stream before network transmission, or constrain the interface to implementations that guarantee this invariant.
document/bridge/client.go:366 — Artifact count and aggregate result-size limits are enforced only after all results have been decoded and fetched, permitting excessive downloads and allocations. Preflight declared counts and sizes, then enforce an overflow-safe remaining-byte budget during decoding and fetching.
internal/processing/source_metadata.go:337 — XML text is copied into every open ancestor without depth or cumulative-memory limits, enabling severe allocation amplification. Bound nesting depth and buffered text, preferably collecting only recognized metadata fields, and abort safely with a warning when limits are exceeded.
Verdict: One high-severity security issue and four medium-severity correctness and resource-management issues require attention.
High
Plaintext provider origins expose documents and credentials Locations:document/docling/client.go:678, document/bridge/client.go:546
Both validators permit arbitrary non-loopback http:// origins under operator_network. Clients may then transmit complete documents and configured API credentials over an unauthenticated connection, allowing network-positioned attackers to intercept or alter documents, steal credentials, or forge rendition responses. Fix: Require HTTPS for all non-loopback origins. Restrict development plaintext to literal loopback addresses and use a transport pinned to loopback CIDRs, matching the GLM-OCR pattern.
Medium
Stale retry entries can cause continuous database polling and prevent checksum-job termination Locations:cmd/docbank/daemon.go:167, cmd/docbank/backfill_retry.go:38
Retry entries are removed only after successful retries. If a failed target disappears or becomes ineligible, its expired entry remains indefinitely, causing empty sweeps to return zero delay and continuously query the database. Fix: Reconcile retry entries after every complete sweep, remove keys no longer returned or eligible, and apply a nonzero idle delay to empty sweeps.
Bridge operations can continue beyond authorization expiry Location:document/bridge/client.go:124
Rendering is bounded only by totalTimeout; polling and artifact retrieval do not stop when authorization.ExpiresAt is reached. Fix: Set the operation deadline to the earliest of the caller deadline, total timeout, and authorization expiry. Enforce expiry before submission, polling, and artifact retrieval.
Historical content versions can return metadata belonging to later versions Location:internal/store/source_metadata.go:194
Historical reads join the current node name/path and latest active provenance by node_id. After replacement, rename, or move, an old version can therefore be reported with attachment and ingest facts from a later version. Fix: Associate provenance and attachment facts with the content version that introduced them and resolve facts using the requested version.
Deeply nested XML can cause quadratic CPU and memory consumption Location:internal/processing/source_metadata.go:337
Every character-data token is copied into every open ancestor’s builder without depth or cumulative-size limits, potentially exhausting resources while the maintenance mutation gate is held. Fix: Bound XML depth and accumulated text, and collect character data only for metadata elements that are consumed.
Code review found two high-severity security/resource-exhaustion risks and one medium-severity authorization-expiry issue.
High
Unbounded artifact downloads before policy validation — document/bridge/client.go:387
Bridge artifacts are downloaded before authorized roles, MaxArtifacts, or MaxTotalResultBytes are enforced. A provider could cause the daemon to retain gigabytes of individually valid artifacts before rejection, potentially exhausting memory. Fix: Preflight artifact declarations against authorization and descriptor limits before fetching, reject excessive counts or cumulative declared sizes, and decrement the remaining result-byte budget during retrieval.
Windows upload spools may inherit readable ACLs — document/upload/spool_windows.go:29
Windows does not translate Mkdir(..., 0o700) and OpenFile(..., 0o600) into owner-only ACLs. When a shared spool root is configured, another local account with inherited access may copy temporary source documents during authorization. Fix: Require and validate an owner-private spool root and apply a current-user-only DACL using internal/winsecurity. If shared roots are supported, atomically create spool directories with an explicit restrictive DACL before writing document bytes.
Medium
Bridge operations can continue after authorization expires — document/bridge/client.go:124
The operation deadline accounts for TotalTimeout but not authorization.ExpiresAt, allowing polling and artifact retrieval after sealed authorization has expired. Fix: Parse ExpiresAt, use the earliest of authorization expiry, caller deadline, and TotalTimeout, and classify authorization-expiry termination as a policy rejection.
document/bridge/client.go:546, document/docling/client.go:678 — Origin validation permits plaintext http:// for any host labeled operator_network, exposing source documents and bearer/API credentials to interception or response tampering. Require HTTPS for non-loopback origins; restrict plaintext to verified loopback destinations using destination-bound transport.
Medium
document/bridge/client.go:124 — Bridge jobs are bounded only by the client timeout, not authorization.ExpiresAt. Jobs can continue making authenticated requests and accept results after authorization expires. Apply the earliest caller/client/authorization deadline, recheck local time before polling and artifact retrieval, and locally validate completion timing.
document/bridge/client.go:387 — Aggregate result limits are enforced only after all artifacts have been downloaded and retained, allowing multi-gigabyte memory exhaustion. Track cumulative evidence, Markdown, and artifact bytes during resolution and reject items before fetching or allocating beyond the remaining budget.
document/datalab/client.go:546 — Spreadsheet evidence locators omit the required stable sheet name, causing otherwise successful renditions to fail canonical validation. Populate Locator.Name from provider metadata or degrade evidence when no stable name is available.
internal/processing/source_metadata.go:174 — metadataCollector.strings does not enforce canonical per-value limits, so oversized metadata can make marshaling fail and trigger repeated generation retries. Enforce entry and aggregate limits during collection, omitting oversized values with a warning.
internal/processing/source_metadata.go:269 — PDF XMP extraction uses the first raw <x:xmpmeta occurrence instead of the active catalog /Metadata stream, potentially selecting stale, orphaned, or page-content data. Resolve metadata through the final cross-reference state and parse only the bounded, appropriately decoded stream.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Docbank now runs the existing Mistral OCR path through the same provider-neutral rendition contract as Docling and Datalab. It verifies one sealed upload, resolves the named credential per attempt, keeps authority expiry active through upload and retries, and converts Mistral pages into complete PDF evidence plus bounded Markdown and a sanitized receipt.
Docbank counts PDF pages locally before egress. Oversized documents, missing returned pages, malformed output, filtered or compressed authority it cannot inspect, and unsupported non-PDF formats fail closed.
Why
Mistral should not bypass the exact source, disclosure, output, and evidence rules just because an OCR client already existed. Provider support also cannot be broader than Docbank’s enforceable local unit authority.
Usage
For library use, construct the adapter with
mistral.NewRenditionProvider(profile, secrets, httpClient)and pass it an authorized PDF. PDF is the only advertised format in this PR; non-PDF input remains blocked until its local unit bounds are enforceable.Daemon, API, and CLI registration is intentionally deferred to S1–S3.
Part of #176 (R9). Stacks on #199.