Skip to content

HYPERFLEET-1375 - fix: validate typed search fields and close DB column leaks#308

Open
mliptak0 wants to merge 2 commits into
openshift-hyperfleet:mainfrom
mliptak0:HYPERFLEET-1375
Open

HYPERFLEET-1375 - fix: validate typed search fields and close DB column leaks#308
mliptak0 wants to merge 2 commits into
openshift-hyperfleet:mainfrom
mliptak0:HYPERFLEET-1375

Conversation

@mliptak0

@mliptak0 mliptak0 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

What

  • Added type validation for typed (non-text) search columns — generation (INTEGER) and created_time/updated_time/deleted_time (TIMESTAMPTZ) — so a mismatched search value (e.g. generation='abc', created_time='not-a-date') is rejected before it reaches Postgres, on both operand orders (generation='abc' and 'abc'=generation).
  • Removed the dead properties.xxx search field mapping, a leftover alias from before the resources table's JSONB column was renamed to spec — it pointed at a nonexistent column and produced the same class of raw pq: leak.
  • Capped the label-key length check at the actual resource_labels.key DB column size (255) instead of the Kubernetes spec's own limit (317), so a spec-valid-but-oversized key is rejected with a clean 400 during request validation instead of a 500 during resource conversion. Could turn the sql column into text to be able to use 317char limit, but since it's not typical case, chosed to not proceed with this change at the moment.

Why

Callers should get a consistent, actionable HYPERFLEET-VAL-* 400 error for bad search input — never a raw Postgres error message or SQLSTATE code. This was a coverage gap found while validating Generic Resources CRUD journeys.

Test Plan

  • Unit tests added/updated
  • make test-all passes
  • make lint passes
  • Helm chart changes validated with make test-helm (if applicable)
  • Deployed to a development cluster and verified (if Helm/config changes)
  • E2E tests passed (if cross-component or major changes)

Manually verified against a live dev cluster: valid/invalid generation and created_time/updated_time/deleted_time searches on both operand orders, IN lists, date-literal-for-timestamp, properties.xxx rejection on /clusters and /resources, an oversized-but-K8s-valid label key on cluster create, and a malformed TSL query — all returned clean HYPERFLEET-VAL-* 400s with no pq:/SQLSTATE leakage.

…mn leaks

Search values for generation/created_time/updated_time/deleted_time reached
Postgres without type validation, leaking raw pq: driver errors and SQLSTATE
codes as 500s instead of clean 400s. Add type validation to FieldNameWalk,
the shared search-to-SQL translation layer used by both /resources and typed
entity search.

Also remove the dead "properties." field mapping, a leftover alias from
before the resources table's JSONB column was renamed to "spec" — it pointed
at a nonexistent column and produced the same class of raw pq: leak.

Also cap the label-key length check at the actual resource_labels.key DB
column size (255) instead of the Kubernetes spec's own limit (317), so a
spec-valid-but-oversized key is rejected with a clean 400 during request
validation instead of a 500 during resource conversion.

Co-Authored-By: Claude <noreply@anthropic.com>
@openshift-ci
openshift-ci Bot requested review from ciaranRoche and ldornele July 23, 2026 10:11
@openshift-ci

openshift-ci Bot commented Jul 23, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign jsell-rh for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Search field mapping now rejects the legacy properties.<key> syntax and validates numeric, integer-like timestamp, and date literals before SQL casting. Unit and integration tests cover invalid and valid typed queries, IN lists, clean validation errors, and both search endpoints. Label validation now uses api.MaxLabelKeyLen and rejects oversized Kubernetes-valid keys before database conversion.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SearchEndpoint
  participant FieldNameWalk
  participant Database
  Client->>SearchEndpoint: Search query
  SearchEndpoint->>FieldNameWalk: Parse and map fields
  FieldNameWalk-->>SearchEndpoint: Validation error or SQL expression
  SearchEndpoint->>Database: Execute validated query
  Database-->>SearchEndpoint: Results
  SearchEndpoint-->>Client: HTTP response
Loading

Suggested reviewers: rh-amarin

🚥 Pre-merge checks | ✅ 11
✅ Passed checks (11 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Sec-02: Secrets In Log Output ✅ Passed No added log statements in non-test files; no token/password/credential/secret fields or interpolated strings found. CWE-532 not triggered.
No Hardcoded Secrets ✅ Passed No hardcoded credentials, embedded creds, private keys, or long base64 literals in touched files; only test strings/comments and validation constants. CWE-798/CWE-259 not present.
No Weak Cryptography ✅ Passed No banned crypto, custom crypto, or secret/HMAC comparisons appear in the PR diff; changes are limited to search/label validation. CWE-327/CWE-208 not implicated.
No Injection Vectors ✅ Passed PASS: Changed SQL paths whitelist identifiers and bind values with sq.Expr; no exec.Command, template.HTML, or yaml.Unmarshal in touched code.
No Privileged Containers ✅ Passed No manifest, Helm, or Dockerfile changes in the diff; only Go code/tests changed, so no new privileged-container settings were introduced (CWE-269).
No Pii Or Sensitive Data In Logs ✅ Passed PASS: No slog/log/zap/fmt.Print* additions in touched files; only validation and tests changed, so no CWE-532 log exposure path.
Title check ✅ Passed The title matches the main change: typed search-field validation and removal of the legacy DB-leak path.
Description check ✅ Passed The description is directly related to the changeset and accurately covers validation, legacy mapping removal, and label-length fixes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@hyperfleet-ci-bot

hyperfleet-ci-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

Risk Score: 1 — risk/low

Signal Detail Points
PR size 391 lines (>200) +1
Sensitive paths none +0
Test coverage Tests cover changed packages +0

Computed by hyperfleet-risk-scorer

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/integration/search_field_mapping_test.go (1)

919-925: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate assertCleanBadRequest closure across two test functions.

Identical helper defined in both TestSearchTypedFieldValidation and TestSearchPropertiesFieldRejected. Extract to a package-level test helper (with t.Helper()) to avoid drift.

Also applies to: 1020-1026

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/integration/search_field_mapping_test.go` around lines 919 - 925,
Extract the duplicated assertCleanBadRequest closure from
TestSearchTypedFieldValidation and TestSearchPropertiesFieldRejected into a
package-level test helper. Give the helper a *testing.T parameter, call
t.Helper(), and preserve the existing status-code and response-body assertions;
update both tests to use it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/db/sql_helpers.go`:
- Around line 585-613: Update validateTypedFieldValues to detect tracked typed
fields on either operand of a binary expression, not only when n.Left is the
identifier. When the right operand is the field, validate the left operand
against that field’s expected kind; preserve the existing left-field validation
and recursive traversal behavior.

---

Nitpick comments:
In `@test/integration/search_field_mapping_test.go`:
- Around line 919-925: Extract the duplicated assertCleanBadRequest closure from
TestSearchTypedFieldValidation and TestSearchPropertiesFieldRejected into a
package-level test helper. Give the helper a *testing.T parameter, call
t.Helper(), and preserve the existing status-code and response-body assertions;
update both tests to use it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 48ef8c1d-eca3-4934-ab97-893befeb4aba

📥 Commits

Reviewing files that changed from the base of the PR and between 913cd01 and f688257.

📒 Files selected for processing (6)
  • pkg/db/sql_helpers.go
  • pkg/db/sql_helpers_test.go
  • pkg/handlers/validation.go
  • pkg/handlers/validation_test.go
  • pkg/services/generic_test.go
  • test/integration/search_field_mapping_test.go
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • openshift-hyperfleet/architecture (manual)
  • openshift-hyperfleet/hyperfleet-api (manual)
  • openshift-hyperfleet/hyperfleet-sentinel (manual)
  • openshift-hyperfleet/hyperfleet-adapter (manual)
  • openshift-hyperfleet/hyperfleet-broker (manual)

Comment thread pkg/db/sql_helpers.go
… side

validateTypedFieldValues only checked the field-on-left form (field = value),
so field-on-right comparisons like 'not-a-date' < created_time or
'abc' = generation skipped validation and reached Postgres unvalidated,
reintroducing the pq:/SQLSTATE leak this PR is meant to close. Also collapses
the per-type error-message switch into a single lookup map for a simpler,
still field-type-aware error message.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant