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
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions