{"record":{"id":"b6d15f2360e52f72","repo":"siyuan-note/siyuan","slug":"invalid-plugin-secure-json-response","errorCode":null,"errorMessage":"invalid plugin secure JSON response","messagePattern":"invalid plugin secure JSON response","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/plugin_service_protocol.go","lineNumber":226,"sourceCode":"\t\tif !json.Valid(payload) {\n\t\t\treturn fmt.Errorf(\"invalid plugin JSON response\")\n\t\t}\n\tcase PluginServiceJSONP:\n\t\tvalid := json.Valid(payload)\n\t\tif tail, ok := strings.CutSuffix(string(payload), \");\"); ok {\n\t\t\tfor index, char := range tail {\n\t\t\t\tif char == '(' && json.Valid([]byte(tail[index+1:])) {\n\t\t\t\t\tvalid = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !valid {\n\t\t\treturn fmt.Errorf(\"invalid plugin JSONP response\")\n\t\t}\n\tcase PluginServiceSecureJSON:\n\t\tif !json.Valid(payload) && !json.Valid([]byte(strings.TrimPrefix(string(payload), \"while(1);\"))) {\n\t\t\treturn fmt.Errorf(\"invalid plugin secure JSON response\")\n\t\t}\n\tcase PluginServiceWebSocket:\n\t\tif status == 101 && len(payload) != 0 {\n\t\t\treturn fmt.Errorf(\"WebSocket handshake contains a body\")\n\t\t}\n\tcase PluginServiceXML:\n\t\tdecoder := xml.NewDecoder(strings.NewReader(string(payload)))\n\t\tfor {\n\t\t\tif _, err := decoder.Token(); err != nil {\n\t\t\t\tif err == io.EOF {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\tcase PluginServiceYAML:\n\t\tvar value yaml.Node\n\t\tif err := yaml.Unmarshal(payload, &value); err != nil {","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/plugin_service_protocol.go#L208-L244","documentation":"Endpoints declared with PluginServiceSecureJSON may return either raw JSON or JSON prefixed with the JSON-hijacking guard \"while(1);\". ValidatePluginServiceResponse accepts the payload only if the raw bytes are valid JSON or the bytes remain valid JSON after stripping that exact prefix; otherwise it returns \"invalid plugin secure JSON response\". The error means the secure-JSON contract is violated — the client would neither parse it directly nor after removing the guard prefix.","triggerScenarios":"Calling Bundle.ValidatePluginServiceResponse with mode PluginServiceSecureJSON and a payload that fails json.Valid both as-is and after strings.TrimPrefix of \"while(1);\" — e.g. a different guard prefix, concatenated JSON values, or truncated output.","commonSituations":"The handler writes a custom anti-hijack prefix (e.g. \")]}'\",\\n\" or \"while(1)\" without the semicolon) that the validator does not recognize; the response is truncated; two JSON documents are written back to back; an error page replaces the JSON body.","solutions":["Use exactly the supported guard prefix \"while(1);\" immediately followed by valid JSON, or emit no prefix at all","Verify the payload with json.Valid on both the raw and trimmed forms before returning it","Remove any custom/legacy security prefixes such as \")]}'\",\\n\" that this validator does not accept","Check for truncated or duplicated writes in the handler that corrupt the JSON body"],"exampleFix":"// before\nw.Write([]byte(\")]}'\\\"\\n\" + string(badJSON)))\n// after\ninner, _ := json.Marshal(data)\nw.Write([]byte(\"while(1);\" + string(inner)))","handlingStrategy":"validation","validationCode":"func isValidSecureJSON(payload []byte) bool {\n  return json.Valid(payload) || json.Valid([]byte(strings.TrimPrefix(string(payload), \"while(1);\")))\n}","typeGuard":null,"tryCatchPattern":"if err := bundle.ValidatePluginServiceResponse(method, path, PluginServiceSecureJSON, status, ct, payload); err != nil { if strings.Contains(err.Error(), \"invalid plugin secure JSON response\") { fixPrefixAndRevalidate(payload); return }; return err }","preventionTips":["Use exactly the \"while(1);\" prefix (with semicolon) when enabling the hijacking guard","Marshal with json.Marshal before prepending the prefix","Avoid legacy prefixes like \")]}'\" that this contract does not accept","Add a golden-file test for secure JSON responses"],"tags":["json","security","validation","plugin-api"],"backgroundTag":"invalid-json-response","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"}