{"record":{"id":"576309adf517d7ab","repo":"basecamp/fizzy","slug":"unexpected-end-of-input","errorCode":null,"errorMessage":"Unexpected end of input","messagePattern":"Unexpected end of input","errorType":"exception","errorClass":"ActionPack::WebAuthn::InvalidCborError","httpStatus":null,"severity":"error","filePath":"lib/action_pack/web_authn/cbor_decoder.rb","lineNumber":112,"sourceCode":"    #   # => {\"a\" => 1, \"b\" => 2}\n    def decode(bytes, **args)\n      bytes = bytes.bytes if bytes.respond_to?(:bytes)\n      new(bytes, **args).decode\n    end\n  end\n\n  def initialize(bytes, max_depth: MAX_DEPTH, max_size: MAX_SIZE) # :nodoc:\n    raise ActionPack::WebAuthn::InvalidCborError, \"Input exceeds maximum size\" if bytes.length > max_size\n\n    @bytes = bytes\n    @max_depth = max_depth\n    @position = 0\n    @depth = 0\n  end\n\n  # Decodes the next CBOR data item from the byte sequence.\n  def decode\n    raise ActionPack::WebAuthn::InvalidCborError, \"Unexpected end of input\" if @position >= @bytes.length\n    raise ActionPack::WebAuthn::InvalidCborError, \"Maximum nesting depth exceeded\" if @depth >= @max_depth\n\n    @depth += 1\n\n    result = case major_type\n    when UNSIGNED_INTEGER_TYPE then decode_unsigned_integer\n    when NEGATIVE_INTEGER_TYPE then decode_negative_integer\n    when BYTE_STRING_TYPE then decode_byte_string\n    when TEXT_STRING_TYPE then decode_text_string\n    when ARRAY_TYPE then decode_array\n    when MAP_TYPE then decode_map\n    when TAG_TYPE then decode_tag\n    when FLOAT_OR_SIMPLE_TYPE then decode_float_or_simple\n    end\n\n    @depth -= 1\n    result\n  end","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/basecamp/fizzy/blob/7aabe7458060d8a1759a53b7ede39e74e6c0b20d/lib/action_pack/web_authn/cbor_decoder.rb#L94-L130","documentation":"CborDecoder#decode raises InvalidCborError when the read position is at or past the end of the buffer. At the top level this means empty input; inside nested structures it means a definite-length array or map declared more elements than the buffer actually contains.","triggerScenarios":"CborDecoder.decode('') or decode([]); an array header like \\x83 (3 items) with only 2 items in the buffer; a map header claiming N pairs where the buffer ends early; trailing break-code confusion that leaves position at end when another item is requested.","commonSituations":"Truncated attestation objects fed to the decoder; fixtures built by concatenating fragments; a client that slices CBOR at the wrong offset (e.g. off-by-one after the flags byte); buffers built with String#slice on multibyte strings.","solutions":["Check the input is non-empty before decoding: raise early if bytes.length.zero?.","Validate the payload length against the declared structure (a quick manual parse of the first header byte) in tests.","Regenerate the CBOR with a known encoder and compare byte-for-byte to find the truncation point.","Rescue ActionPack::WebAuthn::InvalidCborError at the boundary and return 400 — truncated client input is not retryable."],"exampleFix":"# before\nvalue = ActionPack::WebAuthn::CborDecoder.decode(params[:attestation_object].to_s)\n\n# after — reject empty/truncated input early with a clear message\nraw = params[:attestation_object].to_s\nreturn render(json: { error: 'attestation object missing' }, status: :bad_request) if raw.blank?\nvalue = ActionPack::WebAuthn::CborDecoder.decode(raw)","handlingStrategy":"validation","validationCode":"return render(json: { error: 'empty CBOR input' }, status: :bad_request) if bytes.nil? || bytes.length.zero?\nvalue = ActionPack::WebAuthn::CborDecoder.decode(bytes)","typeGuard":"def decodable_cbor?(bytes)\n  !bytes.nil? && bytes.length.positive? && bytes.bytesize <= ActionPack::WebAuthn::CborDecoder::MAX_SIZE\nend","tryCatchPattern":"begin\n  value = ActionPack::WebAuthn::CborDecoder.decode(bytes)\nrescue ActionPack::WebAuthn::InvalidCborError => e\n  render json: { error: \"malformed CBOR: #{e.message}\" }, status: :bad_request\nend","preventionTips":["Reject empty payloads at the boundary with a field-specific message.","Round-trip test encoders: decode(encode(x)) == x catches declared-vs-actual count mismatches.","Never build CBOR by string concatenation of fragments."],"tags":["cbor","truncated-data","binary-parsing","webauthn"],"backgroundTag":"truncated-binary-data","analyzedSha":"7aabe7458060d8a1759a53b7ede39e74e6c0b20d","analyzedAt":"2026-08-21T18:33:25.349Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}