Equally valuable as confirmed bugs — saves future investigators time.
(str "UPDATE tenant SET ") in admin/http/tenants.clj:711(:column meta) is sourced from editable-fields, a static ^:private def with hardcoded column names (tenants.clj:194-199). field URL param is whitelisted via (get editable-fields field) and bad-request returned if meta is nil. No injection possible.INTERVAL '" days " days' in admin/db/queries.cljdays originates from helpers/parse-time-range (helpers.clj:16-43), which derives it from either ChronoUnit/DAYS .between (returns Java long) or (:days preset) (static config map). The from/to query params pass a strict regex check \d{4}-\d{2}-\d{2} before reaching the calc. No string can flow into days.[:raw (str "interval '" stale-running-seconds " seconds'")] in workers/document_output.clj:53stale-running-seconds is sourced from resources/com/getorcha/config.edn:337 (literal 600). Static integer, no user-controlled path.[:cast [:lift n] :interval] with :second units. Replace before this becomes a vulnerability if any caller ever passes a request param.app/http/middleware/auth.clj:247buddy.sign.jwt/unsign does take :alg from the supplied opts (which is the JWT header), so an attacker could craft {"alg": "HS256"} to redirect to HMAC verification. However, (public-key request kid) returns a Java PublicKey object; buddy.core.codecs/to-bytes (which KeyParameter. calls) only extends IByteArray to byte arrays, nil, and String. PublicKey → no implementation → IllegalArgumentException → caught by verify-signature's catch → throws "corrupt or manipulated". Attack rejected by type system.:alg to :rs256 rather than rely on type errors. Worth a small refactor.:document/file-path[:in :document/tenant-id tenant-ids] filter, where tenant-ids derives from app.http.identity/tenants (organization-scoped). The presigned URL's path comes from (:document/file-path document) — the same row that passed tenant scope. Cross-tenant access requires altering file-path in DB which requires another vulnerability.wrap-audit-log async-only middleware breaking sync routes (link/api)link/http.clj:90-107 use Reitit's standard middleware which support both sync and async, and the link API handlers all use 3-arity. No 1-arity handlers in this chain. If a sync handler is ever added without updating wrap-audit-log, ArityException would surface immediately.authenticate (auth.clj:111-116)membership-organization-id query lacks :limit 1, but execute-one! returns the first row anyway. If users genuinely can have multiple memberships, the chosen org is non-deterministic but always one the user belongs to — no cross-org leak.acquisition.clj:131-138's catch Throwable t. The pending-sync row is left in :processing state but claim-inbox! reclaims after stuck-processing-timeout-seconds. Eventually consistent — slight delay rather than data loss.bump-completed-runs-to-version! race (engine.clj:392-397)persist-derivation! commit and current-document-version re-read, a concurrent writer could bump version. In practice the engine is invoked one-at-a-time per document via SQS-driven dispatch; concurrent invocations are not the design. Still worth folding the version bump into the same transaction as persist-derivation!.Thread/startVirtualThread swallows the polling exception, but log/warn records it and the audit record (ap_datev_export_audit) still has task-id set. Operators can sweep for audits with task-id set but no terminal status. Not silent in practice.