Skip to content

fix: reject ambiguous inline key-auth config that bypasses duplicate check#434

Open
shreemaan-abhishek wants to merge 2 commits into
masterfrom
sec/finding-015-dupkey-parser-divergence
Open

fix: reject ambiguous inline key-auth config that bypasses duplicate check#434
shreemaan-abhishek wants to merge 2 commits into
masterfrom
sec/finding-015-dupkey-parser-divergence

Conversation

@shreemaan-abhishek

@shreemaan-abhishek shreemaan-abhishek commented Jul 17, 2026

Copy link
Copy Markdown

What this PR does

The Consumer validating webhook extracts the key-auth key from inline credential config with Go's encoding/json struct decoder. Given a duplicate-key object like {"key":123,"key":"K"}, that decoder returns a type-mismatch error, so extractCredentialKey logs and skips the duplicate check entirely (returns "", nil). Downstream cjson (last-wins, no type abort) resolves the same raw bytes to a live key "K".

Net effect: the webhook's only hard-deny credential check is evadable by any Consumer creator who knows a colliding key. They submit an inline config the webhook can't read but ADC/cjson can, and a duplicate active key-auth credential lands, causing consumer-identity confusion downstream. Check-bypass only (attacker must already know the key), calibrated Low.

Fix

Parse inline key-auth config aligned with the downstream cjson semantics (exact-case, string-valued, last-wins) via a token-level walk, and reject the shapes that cause the divergence instead of silently skipping:

  • Duplicate key members → hard admission error.
  • Non-string key (number/bool/object/array) → hard admission error.
  • Genuinely malformed JSON that cjson would also reject → still lenient ("", nil), so existing consumers with broken config are not suddenly denied.
  • Exact-case matching means {"Key":"K"} is ignored (matches cjson, which only reads lowercase key), removing a false-positive divergence in the other direction.

Tests

  • TestConsumerValidator_DenyDuplicateKeyAuthCredential_ParserDivergence drives the {"key":123,"key":"K"} PoC end-to-end through ValidateCreate and asserts admission is denied.
  • TestParseInlineKeyAuthKey table-tests the parser: valid key, duplicate members, number-then-string PoC, non-string, object value, null, missing key, exact-case, malformed, non-object.
go test ./internal/webhook/v1/...   # pass
go vet ./internal/webhook/...        # clean

Fixes FINDING-015 (api7/rfcs#149). Syncs the same fix as apache/apisix-ingress-controller#2807.

Summary by CodeRabbit

  • Bug Fixes
    • Improved validation of inline key-auth credential configurations.
    • Duplicate or ambiguous key fields in inline JSON are now detected and rejected more consistently.
    • Non-string or malformed inline key values no longer bypass validation.
  • Tests
    • Added unit and integration coverage for inline key extraction and duplicate-key rejection behavior.

…check

The Consumer webhook extracts the key-auth "key" from inline credential
config with Go's encoding/json struct decoder. On {"key":123,"key":"K"}
that decoder returns an error, so the webhook silently skips the duplicate
check, while downstream cjson (last-wins, no type abort) resolves the same
bytes to a live key "K". The only hard-deny credential check was evadable
by any Consumer creator who knows a colliding key.

Parse inline config aligned with cjson (exact-case, string-valued,
last-wins) and reject the ambiguous shapes that cause the divergence:
duplicate "key" members and a non-string "key" are hard admission
errors. Genuinely malformed JSON that cjson also rejects stays lenient.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8386f8cd-e726-48c1-bd4f-e31df44e75ce

📥 Commits

Reviewing files that changed from the base of the PR and between 2941c07 and 737cc08.

📒 Files selected for processing (2)
  • internal/webhook/v1/consumer_webhook.go
  • internal/webhook/v1/consumer_webhook_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/webhook/v1/consumer_webhook_test.go
  • internal/webhook/v1/consumer_webhook.go

📝 Walkthrough

Walkthrough

The consumer webhook now uses streaming JSON parsing for inline key-auth credentials, detecting duplicate or non-string key fields while skipping malformed or unusable inputs. Tests cover parser behavior and duplicate-key admission rejection.

Changes

Inline key-auth credential validation

Layer / File(s) Summary
Streaming inline key-auth parser
internal/webhook/v1/consumer_webhook.go
Replaces struct unmarshalling with token-level parsing, duplicate detection, key type validation, and value skipping.
Parser and webhook validation tests
internal/webhook/v1/consumer_webhook_test.go
Adds table-driven parser coverage and a webhook validation test for parser-divergent duplicate keys.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

  • api7/rfcs#149 — Replaces json.Unmarshal-based extraction to address the duplicate-key parser-divergence bypass.
🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning Blocking: the new tests are unit-style only—they call ValidateCreate/parseInlineKeyAuthKey via a fake client, not the real admission/API flow. Add an envtest or webhook-server admission test that submits a Consumer through the real webhook path and verifies the duplicate-key payload is denied.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main fix: rejecting ambiguous inline key-auth config that could bypass duplicate detection.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Check ✅ Passed No issues found; the change only hardens key-auth parsing and adds tests, with no new logging, auth-bypass, ownership, TLS, or secret-handling risks.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sec/finding-015-dupkey-parser-divergence

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

conformance test report - apisix mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-17T11:02:08Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    failedTests:
    - HTTPRouteInvalidBackendRefUnknownKind
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

conformance test report - apisix-standalone mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-17T11:01:48Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.
- core:
    result: partial
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 0
      Passed: 32
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests partially succeeded with 1 test skips. Extended tests partially
    succeeded with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

conformance test report

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-17T11:19:38Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    failedTests:
    - GatewayModifyListeners
    - TLSRouteSimpleSameNamespace
    result: failure
    statistics:
      Failed: 2
      Passed: 9
      Skipped: 0
  name: GATEWAY-TLS
  summary: Core tests failed with 2 test failures.
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    statistics:
      Failed: 1
      Passed: 11
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests failed with 1 test failures.
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.

…alue)

Address review: parseInlineKeyAuthKey extracted a key from truncated or
multi-top-level-value input instead of skipping it, contradicting the
documented cjson-aligned leniency. Guard with json.Valid so only exactly
one well-formed JSON value is parsed; duplicate keys stay valid and are
still caught by the token walk. Add malformed-shape test cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant