basecamp/fizzy · error · ActionPack::WebAuthn::InvalidCborError

Invalid simple value: #{info}

Error message

Invalid simple value: #{info}

What it means

In decode_float_or_simple, major type 7 with additional-info values 20/21/22/23 (false/true/null/undefined) and 25/26/27 (half/single/double floats) is handled; any other info value (0–19 unassigned simple values, 24 one-byte simple value, 28–31 reserved/break) raises InvalidCborError with the offending value interpolated.

Source

Thrown at lib/action_pack/web_authn/cbor_decoder.rb:194

      else
        Hash.new.tap do |hash|
          read_argument.times do
            hash[decode] = decode
          end
        end
      end
    end

    def decode_float_or_simple
      case info = additional_info
      when SIMPLE_FALSE_VALUE then false
      when SIMPLE_TRUE_VALUE then true
      when SIMPLE_NULL_VALUE, SIMPLE_UNDEFINED_VALUE then nil
      when TWO_BYTE_VALUE_FOLLOWS then decode_half_float
      when FOUR_BYTE_VALUE_FOLLOWS then read_bytes(4).pack("C*").unpack1("g")
      when EIGHT_BYTE_VALUE_FOLLOWS then read_bytes(8).pack("C*").unpack1("G")
      else
        raise ActionPack::WebAuthn::InvalidCborError, "Invalid simple value: #{info}"
      end
    end

    def decode_tag
      tag = read_argument
      value = decode

      case tag
      when POSITIVE_BIGNUM_TAG then value.bytes.inject(0) { |n, b| (n << 8) | b }
      when NEGATIVE_BIGNUM_TAG then -1 - value.bytes.inject(0) { |n, b| (n << 8) | b }
      else value
      end
    end

    def decode_half_float
      half = read_bytes(2).pack("C*").unpack1("n")

      sign = (half >> 15) & 0x1

View on GitHub (pinned to 7aabe74580)

Solutions

  1. Hex-dump the bytes around the failure offset (the decoder stops exactly at the bad item) and compare against the expected structure.
  2. Regenerate the payload with a conformant encoder (RFC 8949); never hand-edit CBOR bytes.
  3. Rescue InvalidCborError and reject the whole payload — one bad item means the byte stream is untrustworthy.
  4. If decoding untrusted input, wrap the call so malformed data returns 400, not a 500.

Example fix

# before
value = ActionPack::WebAuthn::CborDecoder.decode(bytes) # raises mid-parse on 0xF8

# after — treat any CBOR syntax violation as bad input
begin
  value = ActionPack::WebAuthn::CborDecoder.decode(bytes)
rescue ActionPack::WebAuthn::InvalidCborError => e
  Rails.logger.info { "Rejected malformed CBOR: #{e.message}" }
  return render json: { error: 'malformed payload' }, status: :bad_request
end
Defensive patterns

Strategy: try-catch

Try / catch

begin
  value = ActionPack::WebAuthn::CborDecoder.decode(bytes)
rescue ActionPack::WebAuthn::InvalidCborError => e
  Rails.logger.info { "Rejected CBOR: #{e.message} at producer #{request.remote_ip}" }
  render json: { error: 'malformed payload' }, status: :bad_request
end

Prevention

When it happens

Trigger: Decoding bytes like 0xF0 (simple value 16), 0xF8 (one-byte simple value follows), 0xFC/0xFD (reserved 28/29), or a stray 0xFF break code where a value was expected — all shapes that real encoders never emit, so the input is corrupt or hand-crafted.

Common situations: Fuzzed or bit-flipped payloads; test vectors built by incrementing header bytes; random binary data passed as CBOR; buffers shifted by an off-by-one so a type-7 header lands mid-byte.

Related errors


AI-assisted analysis of basecamp/fizzy@7aabe74580 (2026-08-21). Data as JSON: /api/errors/5ece847e13401ac6. Report an issue: GitHub.