Skip to content

HYPERFLEET-1237 - feat: add authorization header support for api_call#210

Draft
rh-amarin wants to merge 3 commits into
mainfrom
adapter-jwt
Draft

HYPERFLEET-1237 - feat: add authorization header support for api_call#210
rh-amarin wants to merge 3 commits into
mainfrom
adapter-jwt

Conversation

@rh-amarin

Copy link
Copy Markdown
Contributor

Summary

  • Adds an authorization block to the api_call config (task config level, per-call) that injects an Authorization: Bearer <token> header
  • type: static — token is a Go template string rendered against params (e.g. {{ .apiToken }} sourced from env via params config)
  • type: kubernetes — reads the pod's mounted ServiceAccount token by default; calls the Kubernetes TokenRequest API when audience is set (bound, short-lived token for a specific audience)
  • Auth-block Authorization header always wins over any Authorization header in the headers: list

What changed

File Change
internal/configloader/types.go APICallAuth struct + Authorization field on APICall
internal/configloader/validator.go Cross-field validation: static requires token; kubernetes + audience requires service_account
internal/executor/utils.go Resolves auth token in ExecuteAPICall after header rendering
internal/auth/provider.go TokenProvider interface, StaticProvider, NewTokenProvider factory
internal/auth/kubernetes.go KubernetesProvider: mounted-file or TokenRequest API
internal/hyperfleetapi/mock.go Mock now applies RequestOption funcs (enables header assertions in tests)

Example config

post_actions:
  - name: reportStatus
    api_call:
      method: PUT
      url: /clusters/{{ .clusterId }}/statuses
      authorization:
        type: static
        token: "{{ .apiToken }}"   # param sourced from env

  - name: fetchSecureData
    api_call:
      method: GET
      url: https://internal-service/data
      authorization:
        type: kubernetes
        audience: "internal-service"
        service_account: "hyperfleet-adapter"
        expiration_seconds: 1800

Test plan

  • go test ./internal/auth/... — 8 new unit tests (static rendering, TokenRequest via fake clientset, namespace resolution, error paths)
  • go test ./internal/executor/... -run TestExecuteAPICall_Authorization — 5 new cases (static, template, override, error, kubernetes outside cluster)
  • make test — all existing tests pass
  • make lint — no new lint issues

Refs: HYPERFLEET-1237

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@openshift-ci openshift-ci Bot requested review from aredenba-rh and ma-hill June 23, 2026 16:18
@openshift-ci

openshift-ci Bot commented Jun 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 kuudori 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 Jun 23, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: 5a89c0dd-a953-41df-9160-e5e29b9c570b

📥 Commits

Reviewing files that changed from the base of the PR and between a774db8 and 77b7780.

📒 Files selected for processing (1)
  • docs/deployment.md
🔗 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)

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added bearer token authentication for API calls, supporting static tokens and Kubernetes ServiceAccount tokens, with optional audience binding and configurable expiration.
  • Documentation

    • Expanded API call configuration docs to fully describe the authorization block, including relative vs absolute URL behavior and detailed token generation rules.
  • Bug Fixes

    • Improved mock request handling so supplied request options are applied consistently.
  • Tests

    • Added/extended unit and executor tests covering static rendering, Kubernetes token retrieval, validation, and authorization precedence.

Walkthrough

Adds bearer-token authorization support to APICall execution. A new APICallAuth struct is introduced in configloader/types.go with type (static or kubernetes), token, audience, service account, namespace, and expiration fields. Cross-field validation is added in configloader/validator.go. A new internal/auth package provides a TokenProvider interface with StaticProvider (Go-template-rendered token) and KubernetesProvider (mounted ServiceAccount token or TokenRequest API). ExecuteAPICall in executor/utils.go resolves the token and sets the Authorization: Bearer header, overriding any explicitly provided header. The hyperfleetapi mock is fixed to apply RequestOptions before recording requests. Tests and documentation are added for all paths.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 11
✅ Passed checks (11 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately identifies the main change: adding authorization header support for api_call configuration with bearer token injection.
Description check ✅ Passed The description is directly related to the changeset, covering the authorization block implementation, token types (static and kubernetes), configuration examples, and test coverage.
Docstring Coverage ✅ Passed Docstring coverage is 81.25% 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 log statements in non-test code expose tokens, passwords, credentials, or secrets; all auth errors wrap underlying errors without interpolating token values.
No Hardcoded Secrets ✅ Passed Comprehensive scan of all 10 files in the PR found zero hardcoded secrets. All sensitive data uses Go templates ({{ .param }}) or environment variables; test fixtures use placeholder values (my-*,...
No Weak Cryptography ✅ Passed No banned cryptographic primitives (md5, des, rc4, SHA1 for security) detected. Tokens are generated via Kubernetes TokenRequest API or rendered as Go templates, then transmitted as Bearer header v...
No Injection Vectors ✅ Passed No CWE-89/CWE-78/CWE-79/CWE-502 injection patterns found. Code uses trusted Kubernetes API, YAML decoder (safe), template rendering with validation, no exec/SQL/unsafe HTML. Config values (KubeConf...
No Privileged Containers ✅ Passed PR contains only Go source code and documentation changes. No Kubernetes manifests, Helm templates, Dockerfiles, or container deployment configurations were modified.
No Pii Or Sensitive Data In Logs ✅ Passed No logging statements expose tokens, PII, session IDs, or sensitive customer data. Token values are never logged; error messages use contextual wrapping without exposing credentials; URLs logged ar...

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch adapter-jwt
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch adapter-jwt

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

@hyperfleet-ci-bot

hyperfleet-ci-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

Risk Score: 3 — risk/medium

Signal Detail Points
PR size 622 lines (>500) +2
Sensitive paths none +0
Test coverage Missing tests for: internal/configloader internal/hyperfleetapi +1

Computed by hyperfleet-risk-scorer

rh-amarin and others added 2 commits June 23, 2026 18:20
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… authorization

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/executor/utils.go (1)

107-127: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Normalize header keys before overriding Authorization (CWE-178).

The override at Line 126 only replaces exact "Authorization". A user-provided "authorization" key can survive and violate the “authorization block takes precedence” contract.

Proposed fix
+import "net/textproto"
@@
-	for _, h := range apiCall.Headers {
+	for _, h := range apiCall.Headers {
 		headerValue, headerErr := utils.RenderTemplate(h.Value, execCtx.Params)
 		if headerErr != nil {
 			return nil, url, fmt.Errorf("failed to render header '%s' template: %w", h.Name, headerErr)
 		}
-		headers[h.Name] = headerValue
+		headers[textproto.CanonicalMIMEHeaderKey(h.Name)] = headerValue
 	}
@@
-		headers["Authorization"] = "Bearer " + token
+		headers[textproto.CanonicalMIMEHeaderKey("Authorization")] = "Bearer " + token
 	}
🤖 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 `@internal/executor/utils.go` around lines 107 - 127, The code fails to handle
case-insensitive HTTP header names, allowing a user-provided "authorization" or
"AUTHORIZATION" header to bypass the authorization override logic (only
"Authorization" is checked). To fix this, normalize all header keys to a
consistent case (e.g., lowercase or title case) when populating the headers map
in the loop where apiCall.Headers are processed, and apply the same
normalization when setting the Authorization header at the line where
headers["Authorization"] = "Bearer " + token is executed. This ensures that
regardless of the case used in user-provided headers, the authorization token
will properly override any conflicting header.
🤖 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 `@internal/auth/kubernetes.go`:
- Around line 39-44: The code at the os.ReadFile call for mountedTokenPath and
the subsequent return string(data), nil statement does not validate that the
token is non-empty or contains only whitespace before returning it. Add
validation after reading the token data to ensure it is not empty and does not
contain only whitespace characters, returning an appropriate error if validation
fails. Apply the same validation fix at line 81 where another token is read and
returned to ensure consistent token validation across all token reading paths.

In `@internal/auth/provider_test.go`:
- Around line 83-90: The test `TestKubernetesProvider_MountedToken_MissingFile`
and `TestResolveNamespace` depend on external filesystem state (the presence or
absence of mounted ServiceAccount token files), causing them to flake in CI
environments where these files may exist. Make these tests deterministic by
either mocking or controlling the mounted token file path through temporary
directories or environment variable manipulation in the test setup, or
alternatively convert these tests to integration tests by adding the //go:build
integration build tag and moving them to the appropriate test/integration
directory as per the project standards.

In `@internal/auth/provider.go`:
- Around line 47-52: The code does not validate that the rendered token is
non-empty before returning the StaticProvider, which could result in invalid
empty bearer tokens being used. After the utils.RenderTemplate call successfully
renders the token, add validation to check that the rendered value is not empty
or whitespace-only before returning the StaticProvider instance. If validation
fails, return an appropriate error message indicating the rendered token is
empty or invalid, preventing the StaticProvider from being created with an
invalid token.

In `@internal/configloader/types.go`:
- Line 260: The ExpirationSeconds field in the struct definition currently
accepts zero and negative values without validation, deferring failure to
runtime. Add validation logic in the configuration parsing phase (where the YAML
is unmarshaled into this struct) to reject non-positive values for
ExpirationSeconds immediately at the schema boundary. This should be done by
implementing or enhancing validation in the config loader to check that if
ExpirationSeconds is provided, it must be greater than zero, ensuring invalid
configurations fail at parse time rather than at runtime token acquisition.

In `@internal/executor/post_action_executor_test.go`:
- Around line 715-721: The test case "kubernetes type without audience fails
outside cluster" is non-deterministic because it relies on the actual runner
environment, potentially passing in-cluster due to mounted SA files. Either mock
the Kubernetes authentication environment to explicitly simulate an
out-of-cluster scenario with no credentials available, or move this test to the
integration test suite and tag it with //go:build integration to make it clear
it requires specific environment conditions. This will ensure the test behavior
is consistent and deterministic regardless of where it runs.

In `@internal/executor/utils.go`:
- Line 118: The call to auth.NewTokenProvider on line 118 dereferences
execCtx.Config.Clients.Kubernetes without first checking if execCtx or
execCtx.Config are nil, which will cause a panic if either is nil. Add guard
checks before this line to verify that execCtx is not nil and that
execCtx.Config is not nil, and return an appropriate error if either check fails
before attempting to access the nested Kubernetes field.

---

Outside diff comments:
In `@internal/executor/utils.go`:
- Around line 107-127: The code fails to handle case-insensitive HTTP header
names, allowing a user-provided "authorization" or "AUTHORIZATION" header to
bypass the authorization override logic (only "Authorization" is checked). To
fix this, normalize all header keys to a consistent case (e.g., lowercase or
title case) when populating the headers map in the loop where apiCall.Headers
are processed, and apply the same normalization when setting the Authorization
header at the line where headers["Authorization"] = "Bearer " + token is
executed. This ensures that regardless of the case used in user-provided
headers, the authorization token will properly override any conflicting header.
🪄 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: Enterprise

Run ID: 52dadf88-bf6a-4158-b158-b99e5ef120c4

📥 Commits

Reviewing files that changed from the base of the PR and between 2d82a5b and a774db8.

📒 Files selected for processing (9)
  • docs/adapter-authoring-guide.md
  • internal/auth/kubernetes.go
  • internal/auth/provider.go
  • internal/auth/provider_test.go
  • internal/configloader/types.go
  • internal/configloader/validator.go
  • internal/executor/post_action_executor_test.go
  • internal/executor/utils.go
  • internal/hyperfleetapi/mock.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 on lines +39 to +44
data, err := os.ReadFile(mountedTokenPath)
if err != nil {
return "", fmt.Errorf("failed to read mounted ServiceAccount token from %s: %w", mountedTokenPath, err)
}
return string(data), nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Validate Kubernetes-issued/mounted tokens are non-empty before return (CWE-20).

Line 39-44 and Line 81 can return empty/whitespace tokens, which later produce malformed bearer auth.

Suggested fix
 import (
 	"context"
 	"fmt"
 	"os"
+	"strings"
@@
 func readMountedToken() (string, error) {
 	data, err := os.ReadFile(mountedTokenPath)
 	if err != nil {
 		return "", fmt.Errorf("failed to read mounted ServiceAccount token from %s: %w", mountedTokenPath, err)
 	}
-	return string(data), nil
+	token := strings.TrimSpace(string(data))
+	if token == "" {
+		return "", fmt.Errorf("mounted ServiceAccount token is empty at %s", mountedTokenPath)
+	}
+	return token, nil
 }
@@
-	return tr.Status.Token, nil
+	token := strings.TrimSpace(tr.Status.Token)
+	if token == "" {
+		return "", fmt.Errorf("token request returned empty token for ServiceAccount %s/%s (audience=%s)",
+			p.namespace, p.serviceAccount, p.audience)
+	}
+	return token, nil

As per path instructions: “Validate input at system boundaries (HTTP handlers, CLI parsers, webhook receivers).”

Also applies to: 81-81

🤖 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 `@internal/auth/kubernetes.go` around lines 39 - 44, The code at the
os.ReadFile call for mountedTokenPath and the subsequent return string(data),
nil statement does not validate that the token is non-empty or contains only
whitespace before returning it. Add validation after reading the token data to
ensure it is not empty and does not contain only whitespace characters,
returning an appropriate error if validation fails. Apply the same validation
fix at line 81 where another token is read and returned to ensure consistent
token validation across all token reading paths.

Source: Path instructions

Comment on lines +83 to +90
// TestKubernetesProvider_MountedToken_MissingFile verifies the error when
// the mounted SA token file is absent (expected outside a real cluster).
func TestKubernetesProvider_MountedToken_MissingFile(t *testing.T) {
p := &KubernetesProvider{audience: ""} // no audience → reads mounted file
_, err := p.GetToken(context.Background())
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to read mounted ServiceAccount token")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unit tests rely on “outside-cluster” filesystem state and can flake in CI.

TestKubernetesProvider_MountedToken_MissingFile and TestResolveNamespace assume mounted ServiceAccount files are absent. In in-cluster runners, those files exist, so these assertions invert.

Deterministic guard option
 import (
 	"context"
+	"os"
 	"testing"
@@
 func TestKubernetesProvider_MountedToken_MissingFile(t *testing.T) {
+	if _, statErr := os.Stat(mountedTokenPath); statErr == nil {
+		t.Skip("mounted ServiceAccount token exists in this environment")
+	}
 	p := &KubernetesProvider{audience: ""} // no audience → reads mounted file
 	_, err := p.GetToken(context.Background())
@@
 	t.Run("empty namespace reads mounted file — fails outside cluster", func(t *testing.T) {
+		if _, statErr := os.Stat(mountedNamespacePath); statErr == nil {
+			t.Skip("mounted namespace file exists in this environment")
+		}
 		_, err := resolveNamespace("")

As per path instructions, “Integration tests tagged with //go:build integration or in test/integration/.”

Also applies to: 127-139

🤖 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 `@internal/auth/provider_test.go` around lines 83 - 90, The test
`TestKubernetesProvider_MountedToken_MissingFile` and `TestResolveNamespace`
depend on external filesystem state (the presence or absence of mounted
ServiceAccount token files), causing them to flake in CI environments where
these files may exist. Make these tests deterministic by either mocking or
controlling the mounted token file path through temporary directories or
environment variable manipulation in the test setup, or alternatively convert
these tests to integration tests by adding the //go:build integration build tag
and moving them to the appropriate test/integration directory as per the project
standards.

Source: Path instructions

Comment thread internal/auth/provider.go
Comment on lines +47 to +52
rendered, err := utils.RenderTemplate(auth.Token, params)
if err != nil {
return nil, fmt.Errorf("failed to render static token template: %w", err)
}
return &StaticProvider{token: rendered}, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Reject empty rendered static tokens before use (CWE-20, CWE-287).

Line 47-52 returns a static provider even when the rendered token is empty/whitespace. That can silently emit Authorization: Bearer .

Suggested fix
 import (
 	"bytes"
 	"context"
 	"fmt"
 	"os"
+	"strings"
@@
 	case "static":
 		rendered, err := utils.RenderTemplate(auth.Token, params)
 		if err != nil {
 			return nil, fmt.Errorf("failed to render static token template: %w", err)
 		}
-		return &StaticProvider{token: rendered}, nil
+		token := strings.TrimSpace(rendered)
+		if token == "" {
+			return nil, fmt.Errorf("static authorization token rendered empty")
+		}
+		return &StaticProvider{token: token}, nil

As per path instructions: “Validate input at system boundaries (HTTP handlers, CLI parsers, webhook receivers).”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rendered, err := utils.RenderTemplate(auth.Token, params)
if err != nil {
return nil, fmt.Errorf("failed to render static token template: %w", err)
}
return &StaticProvider{token: rendered}, nil
rendered, err := utils.RenderTemplate(auth.Token, params)
if err != nil {
return nil, fmt.Errorf("failed to render static token template: %w", err)
}
token := strings.TrimSpace(rendered)
if token == "" {
return nil, fmt.Errorf("static authorization token rendered empty")
}
return &StaticProvider{token: token}, nil
🤖 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 `@internal/auth/provider.go` around lines 47 - 52, The code does not validate
that the rendered token is non-empty before returning the StaticProvider, which
could result in invalid empty bearer tokens being used. After the
utils.RenderTemplate call successfully renders the token, add validation to
check that the rendered value is not empty or whitespace-only before returning
the StaticProvider instance. If validation fails, return an appropriate error
message indicating the rendered token is empty or invalid, preventing the
StaticProvider from being created with an invalid token.

Source: Path instructions

// The generated token is set as "Authorization: Bearer <token>", overriding any Authorization
// header specified in the headers list.
type APICallAuth struct {
ExpirationSeconds *int64 `yaml:"expiration_seconds,omitempty"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Enforce positive expiration_seconds at config-parse time (CWE-20).

Line 260 accepts 0/negative values today, which defers failure to runtime token acquisition. Reject this at the schema boundary.

Suggested fix
-	ExpirationSeconds *int64 `yaml:"expiration_seconds,omitempty"`
+	ExpirationSeconds *int64 `yaml:"expiration_seconds,omitempty" validate:"omitempty,gt=0"`

As per path instructions: “Validate input at system boundaries (HTTP handlers, CLI parsers, webhook receivers).”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ExpirationSeconds *int64 `yaml:"expiration_seconds,omitempty"`
ExpirationSeconds *int64 `yaml:"expiration_seconds,omitempty" validate:"omitempty,gt=0"`
🤖 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 `@internal/configloader/types.go` at line 260, The ExpirationSeconds field in
the struct definition currently accepts zero and negative values without
validation, deferring failure to runtime. Add validation logic in the
configuration parsing phase (where the YAML is unmarshaled into this struct) to
reject non-positive values for ExpirationSeconds immediately at the schema
boundary. This should be done by implementing or enhancing validation in the
config loader to check that if ExpirationSeconds is provided, it must be greater
than zero, ensuring invalid configurations fail at parse time rather than at
runtime token acquisition.

Source: Path instructions

Comment on lines +715 to +721
name: "kubernetes type without audience fails outside cluster",
authorization: &configloader.APICallAuth{
Type: "kubernetes",
},
params: map[string]interface{}{},
expectError: true,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Outside-cluster assumption makes this test non-deterministic.

The case “kubernetes type without audience fails outside cluster” can pass or fail based on runner environment. In-cluster it may succeed using mounted SA files.

Deterministic failure setup
 		{
-			name: "kubernetes type without audience fails outside cluster",
+			name: "kubernetes token request fails with invalid kubeconfig",
 			authorization: &configloader.APICallAuth{
-				Type: "kubernetes",
+				Type:           "kubernetes",
+				Audience:       "target-service",
+				ServiceAccount: "adapter-sa",
+				Namespace:      "hyperfleet",
 			},
 			params:      map[string]interface{}{},
 			expectError: true,
 		},
@@
-			execCtx := NewExecutionContext(context.Background(), map[string]interface{}{}, &configloader.Config{})
+			cfg := &configloader.Config{}
+			if tt.authorization != nil && tt.authorization.Type == "kubernetes" {
+				cfg.Clients.Kubernetes.KubeConfigPath = "/definitely/missing/kubeconfig"
+			}
+			execCtx := NewExecutionContext(context.Background(), map[string]interface{}{}, cfg)
 			execCtx.Params = tt.params

As per path instructions, “Integration tests tagged with //go:build integration or in test/integration/.”

Also applies to: 736-737

🤖 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 `@internal/executor/post_action_executor_test.go` around lines 715 - 721, The
test case "kubernetes type without audience fails outside cluster" is
non-deterministic because it relies on the actual runner environment,
potentially passing in-cluster due to mounted SA files. Either mock the
Kubernetes authentication environment to explicitly simulate an out-of-cluster
scenario with no credentials available, or move this test to the integration
test suite and tag it with //go:build integration to make it clear it requires
specific environment conditions. This will ensure the test behavior is
consistent and deterministic regardless of where it runs.

Source: Path instructions


// Resolve authorization token — always wins over any manually set Authorization header.
if apiCall.Authorization != nil {
provider, authErr := auth.NewTokenProvider(apiCall.Authorization, execCtx.Config.Clients.Kubernetes, execCtx.Params)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard auth config dereference to avoid panic (CWE-476).

Line 118 dereferences execCtx.Config.Clients.Kubernetes without checking execCtx/execCtx.Config. If authorization is enabled with a nil config path, this will panic instead of returning an error.

Proposed fix
-		provider, authErr := auth.NewTokenProvider(apiCall.Authorization, execCtx.Config.Clients.Kubernetes, execCtx.Params)
+		var k8sCfg configloader.KubernetesConfig
+		if execCtx != nil && execCtx.Config != nil {
+			k8sCfg = execCtx.Config.Clients.Kubernetes
+		}
+		provider, authErr := auth.NewTokenProvider(apiCall.Authorization, k8sCfg, execCtx.Params)

As per path instructions, “Flag nil/bounds access without guards.”

🤖 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 `@internal/executor/utils.go` at line 118, The call to auth.NewTokenProvider on
line 118 dereferences execCtx.Config.Clients.Kubernetes without first checking
if execCtx or execCtx.Config are nil, which will cause a panic if either is nil.
Add guard checks before this line to verify that execCtx is not nil and that
execCtx.Config is not nil, and return an appropriate error if either check fails
before attempting to access the nested Kubernetes field.

Source: Path instructions

@openshift-ci

openshift-ci Bot commented Jun 23, 2026

Copy link
Copy Markdown

@rh-amarin: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/unit 77b7780 link true /test unit

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@rh-amarin rh-amarin marked this pull request as draft June 23, 2026 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant