{"record":{"id":"b4e110e28c7ac388","repo":"antonmedv/fx","slug":"error-from-goja-object-set-panic-err","errorCode":null,"errorMessage":"<error from goja Object.Set> (panic(err))","messagePattern":"<error from goja Object\\.Set> \\(panic\\(err\\)\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/jsonx/to_value.go","lineNumber":63,"sourceCode":"\t\tobj := vm.NewObject()\n\n\t\tif n.HasChildren() {\n\t\t\tit := n\n\t\t\tif it.IsCollapsed() {\n\t\t\t\tit = it.Collapsed\n\t\t\t} else {\n\t\t\t\tit = it.Next\n\t\t\t}\n\n\t\t\tfor it != nil && it != n.End {\n\t\t\t\tunquotedKey, err := utils.Unquote(it.Key)\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\n\t\t\t\terr = obj.Set(unquotedKey, it.ToValue(vm))\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\n\t\t\t\tif it.HasChildren() {\n\t\t\t\t\tit = it.End.Next\n\t\t\t\t} else {\n\t\t\t\t\tit = it.Next\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn obj\n\n\tcase Array:\n\t\tvar arr []any\n\n\t\tif n.HasChildren() {\n\t\t\tit := n\n\t\t\tif it.IsCollapsed() {","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/antonmedv/fx/blob/4f31cd3a0c5d66f1b4290a2719bab14a5cee8ebe/internal/jsonx/to_value.go#L45-L81","documentation":"After unquoting the key, ToValue calls goja's obj.Set(unquotedKey, value). goja returns an error if the property cannot be set — e.g. a key equal to a non-writable built-in like '__proto__' or a symbol-like/invalid property name on a non-extensible object — and this code panics with that error. It converts a JS-engine property-set failure into a Go panic.","triggerScenarios":"Converting an Object node containing the key '__proto__' (goja rejects setting it directly via Set) or otherwise unwritable property names, during ToValue/KeysComplete.","commonSituations":"JSON documents crafted with \"__proto__\" keys (prototype-pollution payloads, e.g. from untrusted web input), or keys colliding with engine internals.","solutions":["Rename or drop the '__proto__' key in the input before conversion.","Sanitize untrusted JSON: reject or strip dangerous keys like __proto__, constructor, prototype.","Pre-process the object with a filter that skips un-settable keys, or build the goja object with DefineData/own map instead.","Recover the panic upstream and report the offending key."],"exampleFix":"// before\n{\"__proto__\": {\"admin\": true}, \"a\": 1}\n// after\n{\"prototype\": {\"admin\": true}, \"a\": 1} // or remove the key entirely","handlingStrategy":"validation","validationCode":"func hasUnsafeKeys(data []byte) bool {\n    var m map[string]json.RawMessage\n    if json.Unmarshal(data, &m) != nil {\n        return true\n    }\n    for k := range m {\n        if k == \"__proto__\" || k == \"constructor\" || k == \"prototype\" {\n            return true\n        }\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":"func safeToValue(n *jsonx.Node, vm *goja.Runtime) (v goja.Value, err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"cannot set property on goja object: %v\", r)\n        }\n    }()\n    return n.ToValue(vm), nil\n}","preventionTips":["Strip or rename __proto__/constructor/prototype keys from untrusted JSON before conversion.","Treat prototype-pollution keys as a security signal and reject the document.","Round-trip through map[string]any with decoder.UseNumber to normalize key sets.","Wrap ToValue in recover to convert goja Set failures into errors."],"tags":["json","goja","object-set","prototype-pollution","panic"],"backgroundTag":"proto-property-set-failed","analyzedSha":"4f31cd3a0c5d66f1b4290a2719bab14a5cee8ebe","analyzedAt":"2026-09-02T02:17:47.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}