{"record":{"id":"001627f08f0883dc","repo":"unslothai/unsloth","slug":"native-path-grant-signature-is-invalid","errorCode":null,"errorMessage":"Native path grant signature is invalid.","messagePattern":"Native path grant signature is invalid\\.","errorType":"validation","errorClass":"NativePathLeaseError","httpStatus":400,"severity":"critical","filePath":"studio/backend/utils/native_path_leases.py","lineNumber":180,"sourceCode":"    *,\n    operation: str,\n    expected_kind: str | None = None,\n    expected_path_type: str | None = None,\n    allowed_suffixes: Iterable[str] | None = None,\n) -> NativePathGrant:\n    if not lease:\n        raise NativePathLeaseError(\"Native path grant is required.\")\n\n    secret = _decode_secret()\n    payload_b64, signature_b64 = _split_lease(lease)\n    expected_signature = hmac.new(\n        secret,\n        payload_b64.encode(\"ascii\"),\n        hashlib.sha256,\n    ).digest()\n    supplied_signature = _b64decode(signature_b64)\n    if not hmac.compare_digest(expected_signature, supplied_signature):\n        raise NativePathLeaseError(\"Native path grant signature is invalid.\")\n\n    payload = _decode_payload(payload_b64)\n    _validate_payload(payload, operation = operation, expected_kind = expected_kind)\n\n    path = Path(str(payload[\"canonical_path\"]))\n    _reject_network_or_device_path(path)\n    try:\n        signed_lstat = os.lstat(path)\n    except OSError as exc:\n        raise NativePathLeaseError(\"Native path is no longer accessible.\") from exc\n    if _stat_module.S_ISLNK(signed_lstat.st_mode):\n        raise NativePathLeaseError(\"Native path is no longer a regular file.\")\n    try:\n        resolved = path.resolve(strict = True)\n    except OSError as exc:\n        raise NativePathLeaseError(\"Native path is no longer accessible.\") from exc\n    _reject_network_or_device_path(resolved)\n    if not _same_native_path(resolved, path):","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/native_path_leases.py#L162-L198","documentation":"The HMAC-SHA256 signature attached to the lease does not match the signature computed over the payload segment using the backend's lease secret. Because the frontend can see but not modify a grant without breaking the HMAC, a signature mismatch means the grant bytes were altered after issuance or were signed with a different secret.","triggerScenarios":"Any tampering with the base64url payload segment (changing canonical_path, expiry, operation, etc.), truncating or editing the dotted lease string, using a grant signed by a different installation/session whose UNSLOTH_STUDIO_NATIVE_PATH_LEASE_SECRET differs, or a backend process whose env secret does not match the one the Tauri shell used at signing time.","commonSituations":"Backend restarted or respawned with a stale/different lease secret in its environment; a lease captured from another machine or a previous app session; manual reconstruction of the lease string in frontend code (e.g. JSON.stringify + custom base64 instead of passing the opaque token); proxy or middleware that re-encodes the request body and mangles the lease.","solutions":["Pass the lease string through completely unmodified — treat it as an opaque token, never parse/rebuild it client-side.","Verify the backend process inherited the same UNSLOTH_STUDIO_NATIVE_PATH_LEASE_SECRET value the Tauri shell set when signing (both come from the managed desktop launcher).","If the desktop app was updated/restarted between grant issuance and use, request a fresh grant from the file-picker and retry.","Ensure no middleware, logging layer, or URL-decoder strips or rewrites characters (especially '.' and base64url '-_' vs '+/' confusion) in the lease."],"exampleFix":"// before (frontend rebuilding the lease — breaks HMAC)\nconst lease = btoa(JSON.stringify(payload)) + \".\" + oldSig;\n\n# after (opaque passthrough)\nconst lease = await window.__native.selectFile(options); // Rust-issued token\nawait api.importModel({ lease });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    grant = verify_native_path_lease(lease, operation=OP)\nexcept NativePathLeaseError as exc:\n    if \"signature is invalid\" in str(exc):\n        log_security_event(\"lease_signature_mismatch\", client_fingerprint)\n        return error_response(403, \"File grant could not be verified. Re-select the file.\")\n    raise","preventionTips":["Never parse, rebuild, or re-serialize the lease client-side; pass it opaquely.","Keep signer (Rust shell) and verifier (backend) on the same lease secret lifecycle — restart both together.","Run automated tests that forward an unmodified dialog-issued grant end-to-end to catch transport mangling.","Audit middleware for body re-encoding or character translation on the lease field."],"tags":["hmac","signature","security","tauri"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}