{"record":{"id":"57c35b818f2b9ee6","repo":"siyuan-note/siyuan","slug":"invalid-import-token","errorCode":null,"errorMessage":"invalid import token","messagePattern":"invalid import token","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/api/import.go","lineNumber":227,"sourceCode":"\t\treturn\n\t}\n\tfor {\n\t\ttoken = gulu.Rand.String(32)\n\t\t_, statErr := os.Stat(stagedSYImportPath(token))\n\t\tif os.IsNotExist(statErr) {\n\t\t\tbreak\n\t\t}\n\t\tif statErr != nil {\n\t\t\treturn \"\", statErr\n\t\t}\n\t}\n\terr = os.Rename(srcPath, stagedSYImportPath(token))\n\treturn\n}\n\nfunc claimStagedSYImport(token string) (path string, err error) {\n\tif !isValidSYImportToken(token) {\n\t\treturn \"\", errors.New(\"invalid import token\")\n\t}\n\tstagedSYImportLock.Lock()\n\tdefer stagedSYImportLock.Unlock()\n\tcleanupStagedSYImports()\n\tsrcPath := stagedSYImportPath(token)\n\tif _, err = os.Stat(srcPath); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\terr = errors.New(\"import task not found or expired\")\n\t\t}\n\t\treturn \"\", err\n\t}\n\tpath = filepath.Join(stagedSYImportDir(), token+\"-importing.zip\")\n\terr = os.Rename(srcPath, path)\n\treturn\n}\n\nfunc cleanupStagedSYImports() {\n\tentries, err := os.ReadDir(stagedSYImportDir())","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/api/import.go#L209-L245","documentation":"claimStagedSYImport validates the opaque token returned by the staging step before renaming the staged .sy zip out of the staging directory. isValidSYImportToken rejects tokens that are not well-formed (wrong charset/length), so a malformed or fabricated token yields 'invalid import token'.","triggerScenarios":"Calling the claim API with a token that was never issued, hand-crafted, truncated, or contains characters outside the allowed token alphabet (e.g. path separators — also a traversal guard).","commonSituations":"Client stores the token incorrectly (URL-encoded/decoded wrongly); retry after the token expired uses a stale/guessed value; attacker probes the endpoint with arbitrary strings.","solutions":["Pass the exact token string returned by the stage/upload response, unmodified.","Verify the token matches isValidSYImportToken's expected format before calling.","If the original response was lost, restart the import to get a fresh token.","Ensure the token is not wrapped in quotes or whitespace from copy/paste."],"exampleFix":"// before\nclaim(\"../../etc/tmp-import.zip\") // invalid\n\n// after\ntoken := stageResponse.data.token\nif !isValidSYImportToken(token) { /* re-stage */ }\nclaim(token)","handlingStrategy":"validation","validationCode":"if (!/^[A-Za-z0-9_-]{6,64}$/.test(token)) throw new Error(\"malformed import token\")","typeGuard":"function isValidImportToken(t) { return typeof t === \"string\" && /^[A-Za-z0-9_-]+$/.test(t) }","tryCatchPattern":"try { await claim(token) }\ncatch (e) { if (e.message === \"invalid import token\") restageAndRetry() else throw e }","preventionTips":["Store the token verbatim from the stage response; never re-encode it","Use the stage+claim pair in one flow without manual copy/paste","Regenerate the token after any failure instead of guessing"],"tags":["import","validation","security","token"],"backgroundTag":"invalid-identifier-format","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}