SFTPGo, SpecifyJS, Casdoor, and token-parser boundary checks¶
Source: hourly offensive-security scan, 2026-07-02. Primary entries: GitHub Advisory Database GHSA-h64p-8h4r-6gfh, GHSA-3vcg-pv95-pq54, GHSA-8882-frvv-92w4, GHSA-j5qp-p44g-2m49, GHSA-xw57-23p8-9wc5, GHSA-5c7w-4wm3-85vw, GHSA-gg9x-qcx2-xmrh, and the Casdoor SAML/JWT set GHSA-fwgq-j9r9-qjgr, GHSA-3w4h-g9f5-j84p, GHSA-mfvp-7p3v-x9mh, GHSA-rgq2-93gj-ffxg, and GHSA-339w-3hqm-9pjc.
These are durable for operators because they expose recurring validation seams: public file-share path prefixes, download content disposition, URL parser and redirect checks, GraphQL template interpolation, empty JWT keys, and SAML/OAuth assertions accepted without binding to the trusted issuer, audience, request, time window, or revocation state.
What changed¶
| Advisory | Component | Boundary | Operator value |
|---|---|---|---|
| GHSA-h64p-8h4r-6gfh / CVE-2026-49244 | SFTPGo >= 2.2.0, <= 2.7.1 public browsable-share partial ZIP download | client-supplied files entries were checked with a raw path-prefix relationship, allowing files outside the shared directory when the canonical path began with the share directory name | Public-share assessments should include sibling-prefix canaries such as share vs share-private, not only ../ traversal. |
| GHSA-3vcg-pv95-pq54 / CVE-2026-49245 | SFTPGo public-share and user file download inline parameter | attacker-controlled stored files could be emitted as active text/html under the SFTPGo web origin instead of forced attachment | Download endpoints are render-origin boundaries; prove with harmless HTML markers and no credential theft. |
| GHSA-8882-frvv-92w4 / CVE-2026-50288 | @asymmetric-effort/specifyjs < 0.2.136 secureFetch | URL parse failures silently returned from validation and allowed the request path to continue | SSRF/URL guard testing must include malformed URLs and parser-error branches, not only blocked schemes. |
| GHSA-j5qp-p44g-2m49 | SpecifyJS secureFetch | only the initial URL was validated before default fetch() redirect following | Treat redirects as a second URL authority decision; capture only owned callback hits. |
| GHSA-xw57-23p8-9wc5 | SpecifyJS localhost checks | loopback filtering covered only localhost and 127.0.0.1, missing forms such as 0.0.0.0, [::1], and the broader 127.0.0.0/8 range | URL allowlist tests should include numeric, IPv6, and alias loopback forms. |
| GHSA-5c7w-4wm3-85vw | SpecifyJS gql template tag | interpolated strings containing GraphQL metacharacters warned but were still concatenated into the query | GraphQL template helpers need variable binding; test interpolation sinks with schema-safe canary fields. |
| GHSA-gg9x-qcx2-xmrh / CVE-2026-49852 | joserfc <= 1.6.7 HMAC JWT verification | HS256/HS384/HS512 verification can accept attacker-forged tokens when the caller supplies None or an empty string as the key | Token-parser tests should include unset-env and empty-key failure modes with disposable tokens only. |
| GHSA-fwgq-j9r9-qjgr / CVE-2026-9090 | Casdoor SAML certificate handling | incoming SAMLResponse certificate material could be trusted instead of the preconfigured IdP certificate | Identity-provider assessments should verify the accepted signing key is the configured trust anchor, not assertion-supplied material. |
| GHSA-3w4h-g9f5-j84p / CVE-2026-9093 | Casdoor SAML audience validation | AudienceRestriction was not enforced for the Casdoor SP | Test cross-SP assertion replay with lab-only IdPs and synthetic users. |
| GHSA-mfvp-7p3v-x9mh / CVE-2026-9098 | Casdoor /api/acs callback | well-formed unsolicited SAML responses could be accepted without matching a prior AuthnRequest | Capture request/response binding decisions, not just successful login state. |
| GHSA-rgq2-93gj-ffxg / CVE-2026-9096 | Casdoor SAML time validation | library time-bound warnings were computed but ignored before issuing a session | SAML harnesses should include expired and not-yet-valid assertion controls. |
| GHSA-339w-3hqm-9pjc / CVE-2026-9097 | Casdoor OAuth token exchange | JWT subject tokens were signature-validated without checking active/revoked state | Token-exchange tests should include revoked canary tokens and state-table evidence. |
Replayable validation boundaries¶
SFTPGo public-share path and render-origin checks¶
- Preconditions: disposable SFTPGo lab, affected version, a public browsable share, synthetic files only, and no real user home directories, keys, backups, or customer uploads.
- For the partial ZIP path boundary, create one shared directory such as
/tmp/sftpgo-lab/shareand a sibling canary path such as/tmp/sftpgo-lab/share-private/marker.txt. Request a partial ZIP using only a marker filename/path that should remain confined to the public share. - Positive evidence: the ZIP includes the sibling marker even though it is outside the public share. Stop at the canary file; do not enumerate directories or request system files.
- For the
inlinerender boundary, place a harmless HTML file containing only a visible marker, then compare normal download behavior with anyinlineparameter path. Positive evidence is same-origin HTML rendering; do not run credential collection, JavaScript exfiltration, or account-pivot payloads. - Negative controls: SFTPGo
>= 2.7.3, directory-boundary-aware realpath checks, and unconditionalContent-Disposition: attachmentfor untrusted files.
SpecifyJS URL and GraphQL guard harness¶
- Preconditions: local SpecifyJS app or unit harness, owned callback endpoint, fake secrets, no internal network targets, and a version before
0.2.136for vulnerable controls. - Build a decision table for
secureFetch: valid HTTPS URL, malformed URL that triggersnew URL()failure,https://owned.example/redirect-to-http,https://owned.example/redirect-to-loopback,http://127.1/,http://0.0.0.0/, andhttp://[::1]/. - Positive evidence: a malformed URL or redirect/loopback variant proceeds when policy claims it should be rejected. Use only owned callbacks or loopback canary services; never probe cloud metadata, cluster APIs, admin panels, or production intranet hosts.
- For
gql, interpolate a synthetic value containing GraphQL metacharacters into a query helper and record whether the final query string is constructed. Positive evidence can be a schema-safe marker field or rejected/accepted decision table; do not query tenant data. - Negative controls: parser errors throw, redirects are rejected or revalidated after every hop, loopback/private-IP canonicalization is complete, and GraphQL user values are passed only through variables.
joserfc empty-key JWT verification check¶
- Preconditions: isolated Python harness using
joserfc <= 1.6.7, a disposable token issuer/verifier, and no production JWT secrets. - Simulate common misconfiguration paths separately: unset environment variable resolved to
None, empty environment variable resolved to"", missing DB/Redis key fallback, and a valid non-empty key. - Generate a canary HS256 token signed with
HMAC(key=b"")and synthetic claims such assub=skillz-empty-key-canary. - Positive evidence: the verifier accepts the canary token when the application-provided key is
Noneor empty. Negative evidence should show the same token rejected with a real key and on a patched or application-hardened verifier. - Do not attempt to forge production sessions, admin roles, SSO tokens, or third-party JWTs.
Casdoor SAML/OAuth assertion-binding harness¶
- Preconditions: lab Casdoor instance, lab IdP, disposable SP configuration, synthetic users, fake signing keys, and no production tenant metadata or sessions.
- Build a decision table for SAML assertions: signed by configured IdP certificate, signed by assertion-supplied attacker certificate, wrong
AudienceRestriction, expiredNotOnOrAfter, futureNotBefore, unsolicited response without a recorded AuthnRequest, and response replay after the flow ends. - Positive evidence: Casdoor issues a lab session for the wrong signing key, wrong audience, invalid time window, unsolicited response, or replayed response.
- For token exchange, mint a disposable subject token, revoke or invalidate it in the lab token table, then attempt exchange. Positive evidence is exchange success after revocation.
- Stop at lab session issuance and decision tables. Do not access real applications, real tenants, customer identity data, or session cookies.
Reporting notes¶
- Lead with the crossed boundary: public share path to outside-share read, stored file to same-origin HTML render, URL parser/redirect to unvalidated fetch, GraphQL interpolation to query construction, empty JWT key to forged token acceptance, or SAML/JWT assertion to unbound login/token exchange.
- Include affected package/version, route or helper, canary-only request details, normalized path/URL/assertion fields, decision table, and fixed-version negative control.
- Keep evidence synthetic. Avoid reading secrets, files outside disposable canary directories, tenant GraphQL data, production identity metadata, live tokens, or user sessions.
Reviewed but not promoted here¶
- GHSA-2944-57xv-2682 is a SpecifyJS
data:URI size limit issue and was not promoted as a standalone workflow because it is resource-exhaustion focused. - GHSA-qcr8-x557-7cp3 covers production console warnings leaking framework state; useful for code hygiene, but it did not add a distinct replayable operator path.
- GHSA-93q6-wwjh-jc6h is a legacy-browser CSS expression sanitizer bypass; note it during render reviews, but modern-browser exploitability is narrow.
- GHSA-3vcg-pv95-pq54 is included only as a render-origin boundary paired with SFTPGo shares; do not overstate it as cookie theft because the advisory notes HttpOnly and session-cookie constraints.