Skip to content

Fix panic on missing or non-map query parameter object - #278

Open
SebTardif wants to merge 4 commits into
pb33f:mainfrom
SebTardif:fix/query-param-panic
Open

SebTardif wants to merge 4 commits into
pb33f:mainfrom
SebTardif:fix/query-param-panic

Conversation

@SebTardif

Copy link
Copy Markdown

Problem

In parameters/query_parameters.go, the helpers.Object case uses a bare type assertion:

encodedObj[params[p].Name].(map[string]interface{})

This panics when:

  1. encodedObj is nil (e.g. content-wrapped parameter with a non-JSON media type skips map initialization)
  2. The key params[p].Name is absent from encodedObj (map lookup returns nil, bare assertion on nil panics)
  3. The value is present but is not a map[string]interface{}

Fix

Replace the bare type assertion with a three-step comma-ok pattern:

if encodedObj == nil {
    break skipValues
}
objVal, objExists := encodedObj[params[p].Name]
if !objExists || objVal == nil {
    break skipValues
}
objMap, mapOk := objVal.(map[string]interface{})
if !mapOk {
    break skipValues
}

This follows the existing control flow: when the value cannot be decoded, we break skipValues (the same pattern used for JSON unmarshal failures a few lines above).

Test

Added TestQueryParamObjectMissingKey_NoPanic which declares an object query parameter with a text/plain content wrapper. This forces the contentWrapped path without JSON decoding, leaving encodedObj nil. Before the fix, this test panics. After the fix, it completes without error.

@codecov

codecov Bot commented May 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.27%. Comparing base (b1808fe) to head (ebf97b2).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #278   +/-   ##
=======================================
  Coverage   98.27%   98.27%           
=======================================
  Files          80       80           
  Lines        9481     9507   +26     
=======================================
+ Hits         9317     9343   +26     
  Misses        139      139           
  Partials       25       25           
Flag Coverage Δ
unittests 98.27% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@daveshanley

Copy link
Copy Markdown
Member

two lines missing coverage... you can do it :)

Comment thread parameters/query_parameters.go Outdated
@SebTardif

Copy link
Copy Markdown
Author

@daveshanley

two lines missing coverage... you can do it :)

Fixed in d002a64. The guards now emit a QueryParameterCannotBeDecoded validation error (modeled after HeaderParameterCannotBeDecoded) instead of silently breaking. Updated the test to assert on the returned error and added a positive test for valid JSON object parameters.

@daveshanley

Copy link
Copy Markdown
Member

Coverage is a big drop-off; need to bring it up! The libs are something I am anal about coverage on.

@SebTardif

Copy link
Copy Markdown
Author

@daveshanley Coverage improved in 069d2e1:

  • errors/parameter_errors.go: QueryParameterCannotBeDecoded now has a direct unit test in the errors/ package (0% to 100% per-package coverage, 98.0% to 98.9% overall).
  • parameters/query_parameters_test.go: Added 5 integration tests covering JSON content-wrapped (valid + invalid schema), form-encoded (valid + invalid), and XML content type paths through the object parameter validation.

The remaining uncovered lines are the bodies of guards 2 and 3 (!objExists || objVal == nil and !mapOk). These are defensive checks: all current encoding helpers (ConstructParamMapFromFormEncodingArrayWithSchema, ConstructParamMapFromDeepObjectEncoding, etc.) always produce a map[string]interface{} value for the parameter key, so the guard bodies are unreachable through the public API. They protect against future changes to the helper functions.

@daveshanley

Copy link
Copy Markdown
Member

Thanks for sticking with this, the fix now returns an error correctly and the full test suite passes at 069d2e1. This is close! A few things to tidy up before we merge:

  • Please simplify the three guards in parameters/query_parameters.go into one checked type assertion and one error path. Reading from a nil map is safe, and a missing key or a non-map value will make the assertion return false:

    objMap, ok := encodedObj[params[p].Name].(map[string]interface{})
    if !ok {
        validationErrors = append(validationErrors,
            errors.QueryParameterCannotBeDecoded(params[p], ef, sch, pathValue, operation, renderedSchema))
        break skipValues
    }

    This keeps the panic protection, removes the repeated error handling, and lets the existing unsupported-content tests exercise the shared rejection path. I understand why the extra branches are currently unreachable; we can avoid carrying those separate branches altogether.

  • Keep the valid JSON/form cases and the invalid schema/unsupported-content cases. Both coverage checks are still red, with patch coverage at 77.78%, so please get the changed code fully covered without lowering the thresholds.

  • Add a short GoDoc comment to the exported QueryParameterCannotBeDecoded function.

  • Please update the branch against current main and rerun the full tests, lint, and coverage. The branch is now 32 commits behind its base on main, so we need fresh results with the current code and dependencies.

Thanks again — a small cleanup and we should be in good shape :)

Replace bare type assertion with comma-ok pattern to prevent panic
when query parameter object key is absent or value is not a map.
The nil/type guards prevented the panic but also silently accepted
requests where a content-wrapped object parameter could not be decoded
into a map. Now each guard emits a QueryParameterCannotBeDecoded
validation error before breaking, so the request correctly fails
validation instead of passing silently.

Add QueryParameterCannotBeDecoded error constructor (modeled after
HeaderParameterCannotBeDecoded) and update the test to assert on the
returned error. Add a positive test for valid JSON object parameters.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
… paths

Add unit test for QueryParameterCannotBeDecoded error constructor in
errors/ package (21 lines, 0% -> 100% per-package coverage).

Add integration tests for query parameter object validation:
- JSON content-wrapped with invalid property type (schema error path)
- Form-encoded valid and invalid objects (non-content-wrapped path)
- XML content type (second encodedObj==nil guard path)

These bring patch coverage from 19% to ~78% and restore the errors/
package from 98.0% to 98.9%.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
A nil map read is safe, so a missing key and a non-map value are the
same failure as a nil decoded object. Keep one checked type assertion
and one QueryParameterCannotBeDecoded path.

Document QueryParameterCannotBeDecoded. The valid JSON and form cases,
plus the invalid schema and unsupported content cases, still cover the
changed lines.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
@SebTardif
SebTardif force-pushed the fix/query-param-panic branch from 069d2e1 to ebf97b2 Compare October 6, 2026 16:59
@SebTardif

Copy link
Copy Markdown
Author

@daveshanley

Please simplify the three guards in parameters/query_parameters.go into one checked type assertion and one error path. Add a short GoDoc comment to the exported QueryParameterCannotBeDecoded function. Please update the branch against current main and rerun the full tests, lint, and coverage.

Done. The decode check is one type assertion. A nil map read is safe, and a missing key or a non-map value takes the same QueryParameterCannotBeDecoded path. That function now has a short GoDoc comment. The branch is rebased onto current main.

The valid JSON and form cases are still there, and so are the invalid schema and unsupported content cases. Locally, go test ./... passed, golangci-lint was clean, and QueryParameterCannotBeDecoded is fully covered. Coverage thresholds are unchanged.

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.

2 participants