{"record":{"id":"3f3e98cb47304b89","repo":"chenhg5/cc-connect","slug":"wecom-ws-invalid-aeskey-base64-length","errorCode":null,"errorMessage":"wecom-ws: invalid aeskey base64 length","messagePattern":"wecom-ws: invalid aeskey base64 length","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wecom/websocket_media.go","lineNumber":243,"sourceCode":"\t\t}\n\t\tif len(key) != 32 {\n\t\t\treturn nil, fmt.Errorf(\"wecom-ws: aeskey hex length %d, want 32 bytes\", len(key))\n\t\t}\n\t\treturn key, nil\n\t}\n\n\t// URL-safe alphabet → standard (RFC 4648 §5)\n\ts = strings.ReplaceAll(s, \"-\", \"+\")\n\ts = strings.ReplaceAll(s, \"_\", \"/\")\n\n\tswitch len(s) % 4 {\n\tcase 0:\n\tcase 2:\n\t\ts += \"==\"\n\tcase 3:\n\t\ts += \"=\"\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"wecom-ws: invalid aeskey base64 length\")\n\t}\n\n\tkey, err := base64.StdEncoding.DecodeString(s)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"wecom-ws: decode aeskey: %w\", err)\n\t}\n\tif len(key) < 32 {\n\t\treturn nil, fmt.Errorf(\"wecom-ws: aeskey decoded length %d, need >= 32\", len(key))\n\t}\n\treturn key, nil\n}\n\nfunc isHexString(s string) bool {\n\tfor i := 0; i < len(s); i++ {\n\t\tc := s[i]\n\t\tswitch {\n\t\tcase c >= '0' && c <= '9', c >= 'a' && c <= 'f', c >= 'A' && c <= 'F':\n\t\tdefault:","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wecom/websocket_media.go#L225-L261","documentation":"decodeWeComAESKey in platform/wecom/websocket_media.go validates the base64-encoded AES key supplied with WeCom websocket media URLs. It only accepts base64 strings whose length mod 4 leaves remainder 0, 2, or 3, appending the missing '=' padding itself; any other length (remainder 1, or absurdly long) cannot be valid padded base64, so it aborts. This guards against garbage keys before attempting a stdlib base64 decode.","triggerScenarios":"Calling wecomDecryptFile (via downloadWeComWSMedia) with an aesKeyB64 string whose length % 4 == 1, e.g. a truncated or corrupted base64 key, a hex string of odd length, or a key with stray characters removed/added.","commonSituations":"Copying the WeCom bot EncodingAESKey out of the admin console and clipping a character; a URL-safe, unpadded key passed through a transformation that stripped padding incorrectly; storing the key in config with whitespace/newline stripped unevenly; passing a hex-encoded key (64 hex chars is fine, 63 is not).","solutions":["Check len(strings.TrimSpace(aesKey)) % 4 != 1 before calling; re-copy the EncodingAESKey from the WeCom admin console in full.","If the key is hex-encoded, convert or pass it through the hex branch the tests cover (isHexString) rather than as base64.","Normalize the key: strip whitespace, convert URL-safe '-_' to '+/' and re-pad with '=' to a multiple of 4 before decrypting."],"exampleFix":"// before\nraw := cfg.AESKey // e.g. truncated \"abcde\" (len 5)\nplain, err := wecomDecryptFile(ciphertext, raw)\n// after\nraw := strings.TrimSpace(cfg.AESKey)\nif len(raw)%4 == 1 {\n    return nil, fmt.Errorf(\"bad aes key length %d\", len(raw))\n}\nplain, err := wecomDecryptFile(ciphertext, raw)","handlingStrategy":"validation","validationCode":"func validAESKeyLen(s string) bool {\n    s = strings.TrimSpace(s)\n    return len(s) > 0 && len(s)%4 != 1\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Paste the EncodingAESKey from the WeCom console without manual edits; strip only outer whitespace.","Assert key length/charset at config-load time, before any media download."],"tags":["wecom","base64","crypto","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}