Skip to content

[Security][High] Path traversal via unsanitized nid and integrity proof serialization mismatch #14

@numbers-official

Description

@numbers-official

Security Findings — High Severity

1. Path Traversal via Unsanitized nid in URL Construction

Files:

  • ts/src/client.ts lines 311, 339
  • python/numbersprotocol_capture/client.py lines 391, 418

Description:
The nid parameter is interpolated directly into URL paths without validation or encoding:

// TypeScript
`${this.baseUrl}/assets/${nid}/`
# Python
f"{self._base_url}/assets/{nid}/"

The only validation is a truthiness check (if (!nid) / if not nid), which accepts any non-empty string. A malicious nid value containing path traversal characters (e.g., ../../admin/users) or URL-special characters (?, #, @) can alter the target URL, potentially directing authenticated requests (with the Authorization header) to unintended API endpoints.

Impact:
An attacker who controls the nid parameter can craft values like ../../other-endpoint to make the SDK send authenticated requests to arbitrary paths on the API server, or with carefully crafted values containing @ or #, potentially redirect to different hosts entirely. This is a server-side request manipulation vector.

Suggested fix:
Validate that nid matches the expected format (IPFS CIDs typically match ^[a-zA-Z0-9]+$) and/or use encodeURIComponent() / urllib.parse.quote() when embedding it in URL paths. Note that verify.ts already uses encodeURIComponent(nid) for query parameters — the same treatment should be applied in client.ts.


2. Integrity Proof JSON Serialization Mismatch Between TypeScript and Python

Files:

  • ts/src/crypto.ts line 44
  • python/numbersprotocol_capture/crypto.py line 71

Description:
The TypeScript signIntegrityProof uses JSON.stringify(proof) which serializes the entire proof object as-is (including any extra properties that may be attached at runtime). The Python sign_integrity_proof explicitly constructs a new dict with exactly three keys in a specific order.

This asymmetry means that if the proof object ever carries additional fields, the TypeScript SDK will silently produce a different integrity hash. The SDKs must produce identical JSON for the same input to ensure cross-language signature interoperability.

Impact:
Cross-language signature verification failures. An asset registered with one SDK could produce mismatched integrity hashes when verified with the other SDK, undermining the core provenance guarantee.

Suggested fix:
In TypeScript, change JSON.stringify(proof) to explicitly construct the object with only the three expected keys in the same order as the Python version:

const proofJson = JSON.stringify({
  proof_hash: proof.proof_hash,
  asset_mime_type: proof.asset_mime_type,
  created_at: proof.created_at,
});

Add a cross-SDK test that verifies both SDKs produce the same integritySha for identical input.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions