{"record":{"id":"a5ab879cf1a188e1","repo":"crowdsecurity/crowdsec","slug":"hex-master-secret-decodes-to-d-bytes-minimum-is","errorCode":null,"errorMessage":"hex master secret decodes to %d bytes; minimum is %d","messagePattern":"hex master secret decodes to (.+?) bytes; minimum is (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/appsec/challenge/secret.go","lineNumber":46,"sourceCode":"\t\treturn nil, fmt.Errorf(\"generate random master secret: %w\", err)\n\t}\n\treturn buf, nil\n}\n\n// ParseConfiguredSecret accepts a configured master secret as either a hex\n// string (preferred — encodes raw bytes unambiguously) or a raw passphrase\n// (fallback for human-edited configs). The result is at least minSecretBytes.\nfunc ParseConfiguredSecret(value string) ([]byte, error) {\n\tif value == \"\" {\n\t\treturn nil, errors.New(\"empty master secret\")\n\t}\n\n\t// Hex form: even length, hex digits only.\n\tif isHex(value) {\n\t\traw, err := hex.DecodeString(value)\n\t\tif err == nil {\n\t\t\tif len(raw) < minSecretBytes {\n\t\t\t\treturn nil, fmt.Errorf(\"hex master secret decodes to %d bytes; minimum is %d\", len(raw), minSecretBytes)\n\t\t\t}\n\t\t\treturn raw, nil\n\t\t}\n\t\t// Fall through to passphrase handling on hex parse failure — defensive.\n\t}\n\n\tif len(value) < minSecretBytes {\n\t\treturn nil, fmt.Errorf(\"passphrase master secret is %d bytes; minimum is %d\", len(value), minSecretBytes)\n\t}\n\n\treturn []byte(value), nil\n}\n\nfunc isHex(s string) bool {\n\tif s == \"\" || len(s)%2 != 0 {\n\t\treturn false\n\t}\n\tfor i := range len(s) {","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/secret.go#L28-L64","documentation":"ParseConfiguredSecret accepts a hex-encoded master secret, and rejects it when the decoded bytes are shorter than minSecretBytes. Hex strings are parsed by character count, so a long-looking hex string can still decode to too little entropy; this error surfaces that directly.","triggerScenarios":"BuildOptions parses a configured master_secret that isHex recognizes and hex.DecodeString decodes, but the decoded byte length is below minSecretBytes — e.g. 40 hex chars = 20 bytes.","commonSituations":"Operator generated a 16/20/24-byte key instead of 32; used an MD5 or SHA-1 hash output as the secret; copy-pasted a partial hex string; test cases like TestParseConfiguredSecret_HexTooShort.","solutions":["Generate a 32-byte secret: `openssl rand -hex 32` (64 hex characters) and paste it as master_secret","Count the hex characters: length/2 must be >= minSecretBytes","If it was intended as a passphrase, remove hex-looking characters so it falls into the passphrase path (still must meet the byte minimum)","Update distributed-deployment configs so every instance gets the corrected full-length secret"],"exampleFix":"// before: 40 hex chars -> 20 bytes -> rejected\nmaster_secret: \"a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0\"\n// after: 64 hex chars -> 32 bytes\nmaster_secret: \"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\"","handlingStrategy":"validation","validationCode":"func validHexSecretLen(s string, min int) bool {\n    raw, err := hex.DecodeString(s)\n    return err == nil && len(raw) >= min\n}\n// use: validHexSecretLen(cfg.MasterSecret, 32)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use `openssl rand -hex 32` to generate secrets","Validate with challenge.ParseConfiguredSecret at config-load time","Document that hex length must be 2x the byte minimum","Add config validation to CI for distributed deployment manifests"],"tags":["crypto","hex","configuration"],"backgroundTag":"invalid-config-value","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}