{"record":{"id":"f02a1b4a1773df64","repo":"fish2018/pansou","slug":"url-f02a1b","errorCode":null,"errorMessage":"加密URL不能为空","messagePattern":"加密URL不能为空","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"plugin/sdso/sdso.go","lineNumber":380,"sourceCode":"\t\tif err == nil && resp.StatusCode == 200 {\n\t\t\treturn resp, nil\n\t\t}\n\n\t\tif resp != nil {\n\t\t\tresp.Body.Close()\n\t\t}\n\t\tlastErr = err\n\t}\n\n\treturn nil, fmt.Errorf(\"重试 %d 次后仍然失败: %w\", maxRetries, lastErr)\n}\n\n// DecryptURL 解密SDSO网站返回的加密URL\n// 输入: Base64编码的密文\n// 输出: 解密后的原始网盘链接\nfunc DecryptURL(encryptedURL string) (string, error) {\n\tif encryptedURL == \"\" {\n\t\treturn \"\", fmt.Errorf(\"加密URL不能为空\")\n\t}\n\n\t// Base64解码\n\tciphertext, err := base64.StdEncoding.DecodeString(encryptedURL)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Base64解码失败: %w\", err)\n\t}\n\n\t// 检查密文长度\n\tif len(ciphertext) == 0 {\n\t\treturn \"\", fmt.Errorf(\"密文长度为0\")\n\t}\n\n\t// 检查密文长度是否为16的倍数\n\tif len(ciphertext)%aes.BlockSize != 0 {\n\t\treturn \"\", fmt.Errorf(\"密文长度不是AES块大小的倍数\")\n\t}\n","sourceCodeStart":362,"sourceCodeEnd":398,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/sdso/sdso.go#L362-L398","documentation":"DecryptURL validates its input before AES decryption and rejects an empty encrypted URL string. Each search result item's URL is passed here; an empty URL means the API returned a list item with no encrypted link, which the plugin skips (counted as skippedCount).","triggerScenarios":"An item in apiResp.Data.List has item.URL == \"\" — the sdso API returned a result record lacking its encrypted link field, and fetchSinglePageWithType calls DecryptURL(item.URL) on it.","commonSituations":"The site's API returning placeholder/incomplete records for some shares; deleted shares still appearing in the index with the URL field blank; API contract changes where the link moved to a different JSON field.","solutions":["This is handled internally: the plugin skips the item and continues, so no caller action is needed.","If many items trigger this, check whether the API moved the encrypted link to a different JSON field and update APIResponse/item structs.","Filter empty-URL items before calling DecryptURL to keep skip counts clean.","Report persistent occurrences as a data-quality issue with the sdso.top source."],"exampleFix":"// before\ndecryptedURL, err := DecryptURL(item.URL)\n// after\nif item.URL == \"\" {\n    skippedCount++\n    continue\n}\ndecryptedURL, err := DecryptURL(item.URL)","handlingStrategy":"validation","validationCode":"if item.URL == \"\" { /* skip item before calling DecryptURL */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Skip empty URL fields before decryption","Watch skip counts for spikes indicating API contract changes","Verify the JSON field holding the encrypted link after site updates"],"tags":["validation","empty-input","decryption","go"],"backgroundTag":"empty-required-field","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}