Merge 1.2.3 to main#78
Conversation
…ve for K8SNS/K8SCluster alias parsing (#77) * fix(management): guard against IndexOutOfRangeException in HandleRemove for K8SNS/K8SCluster alias parsing HandleRemove accessed splitAlias[^2] without a bounds check for both K8SNS and K8SCluster store types. A bare alias without the expected '/' delimiter produced a single-element array, causing 'Index was outside the bounds of the array'. Added Length < 2 guard for K8SNS and Length < 3 guard for K8SCluster; returns a clear FailJob with an actionable error message describing the expected alias format. * Update generated docs * chore(changelog): add 1.2.3 release notes * Update generated docs * chore(changelog): add K8SNS management add fix entry to 1.2.3 release notes * chore(changelog): update 1.2.3 release notes to reference 1.1.4 hotfix fixes * chore(changelog): fix 1.2.3 and add 1.1.4 release notes * Update generated docs --------- Co-authored-by: Keyfactor <keyfactor@keyfactor.github.io>
There was a problem hiding this comment.
Pull request overview
Automated merge of release 1.2.3 into main, primarily updating store-type creation scripts and hardening Kubernetes management “Remove” job alias parsing to avoid IndexOutOfRangeException.
Changes:
- Update PowerShell + Bash scripts to create all 7 Kubernetes-related store types, with added OAuth/Bearer auth support.
- Add alias-format guards in
Management.HandleRemove()forK8SNSandK8SCluster. - Add/refresh documentation images and update
CHANGELOG.mdfor1.2.3.
Reviewed changes
Copilot reviewed 6 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/store_types/powershell/restmethod_create_store_types.ps1 | Reworked REST-based store-type creation script with multiple auth modes and helper function. |
| scripts/store_types/powershell/kfutil_create_store_types.ps1 | Expands kfutil-based script to create all 7 store types and adds clearer prechecks. |
| scripts/store_types/bash/kfutil_create_store_types.sh | Expands kfutil-based Bash script to create all 7 store types and tightens tool checks. |
| scripts/store_types/bash/curl_create_store_types.sh | Reworked curl-based store-type creation script with OAuth support and helper function. |
| kubernetes-orchestrator-extension/Jobs/Management.cs | Adds alias parsing guards in Remove flow for K8SNS/K8SCluster capabilities. |
| docsource/images/K8SNS-basic-store-type-dialog.png | Adds/updates documentation image asset. |
| docsource/images/K8SCluster-basic-store-type-dialog.png | Adds/updates documentation image asset. |
| CHANGELOG.md | Adds 1.2.3 changelog entry (and includes 1.1.4 section). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (splitAlias.Length < 2) | ||
| { | ||
| var errMsg = $"Invalid alias format for K8SNS store type. Expected pattern: 'secrets/<tls|opaque>/<secret_name>' but got '{certAlias}'"; | ||
| Logger.LogError(errMsg); | ||
| return FailJob(errMsg, config.JobHistoryId); | ||
| } | ||
| // Split alias by / and get second to last element KubeSecretType | ||
| KubeSecretType = splitAlias[^2]; | ||
| KubeSecretName = splitAlias[^1]; |
There was a problem hiding this comment.
The validation for the K8SNS remove alias is too lenient for the format described in the error message. With the documented pattern secrets/<tls|opaque>/<secret_name>, splitAlias.Length must be at least 3 (and ideally also validate the fixed secrets segment) before indexing [^2]/[^1]; otherwise aliases like tls/mysecret will be treated as valid and can lead to removing the wrong secret.
| if (splitAlias.Length < 3) | ||
| { | ||
| var errMsg = $"Invalid alias format for K8SCluster store type. Expected pattern: '<namespace>/secrets/<tls|opaque>/<secret_name>' but got '{certAlias}'"; | ||
| Logger.LogError(errMsg); | ||
| return FailJob(errMsg, config.JobHistoryId); | ||
| } | ||
| KubeSecretType = splitAlias[^2]; | ||
| KubeSecretName = splitAlias[^1]; | ||
| KubeNamespace = splitAlias[0]; |
There was a problem hiding this comment.
The K8SCluster alias validation does not match the documented expected pattern. The message indicates <namespace>/secrets/<tls|opaque>/<secret_name> (4 segments), but the code only requires 3 segments; a 3-part alias will pass and then KubeSecretType becomes secrets. Consider requiring at least 4 segments (and validating the secrets segment) before extracting KubeSecretType/KubeSecretName/KubeNamespace.
| elif [ -n "${KEYFACTOR_AUTH_CLIENT_ID}" ] && [ -n "${KEYFACTOR_AUTH_CLIENT_SECRET}" ] && [ -n "${KEYFACTOR_AUTH_TOKEN_URL}" ]; then | ||
| echo "Fetching OAuth token..." | ||
| BEARER_TOKEN=$(curl -s -X POST "${KEYFACTOR_AUTH_TOKEN_URL}" \ | ||
| -H "Content-Type: application/x-www-form-urlencoded" \ | ||
| --data-urlencode "grant_type=client_credentials" \ | ||
| --data-urlencode "client_id=${KEYFACTOR_AUTH_CLIENT_ID}" \ | ||
| --data-urlencode "client_secret=${KEYFACTOR_AUTH_CLIENT_SECRET}" | jq -r '.access_token') | ||
| if [ -z "${BEARER_TOKEN}" ] || [ "${BEARER_TOKEN}" = "null" ]; then | ||
| echo "ERROR: Failed to fetch OAuth token from ${KEYFACTOR_AUTH_TOKEN_URL}" | ||
| exit 1 |
There was a problem hiding this comment.
The OAuth client-credentials path pipes the token response through jq, but the script doesn't verify jq is installed. On systems without jq this will silently produce an empty token and then fail later; either add a command -v jq check with a clear error, or avoid the dependency (e.g., parse with a minimal fallback).
| client_id = $env:KEYFACTOR_AUTH_CLIENT_ID | ||
| client_secret = $env:KEYFACTOR_AUTH_CLIENT_SECRET | ||
| } | ||
| $tokenResp = Invoke-RestMethod -Method Post -Uri $env:KEYFACTOR_AUTH_TOKEN_URL -Body $tokenBody |
There was a problem hiding this comment.
Invoke-RestMethod token acquisition does not validate that an access_token was returned before constructing the Authorization header. If the token endpoint returns an error payload, $tokenResp.access_token can be $null and the script will proceed with an invalid Bearer header. Add a check for a non-empty token (and ideally surface the token endpoint error/HTTP status) before continuing.
| $tokenResp = Invoke-RestMethod -Method Post -Uri $env:KEYFACTOR_AUTH_TOKEN_URL -Body $tokenBody | |
| try { | |
| $tokenResp = Invoke-RestMethod -Method Post -Uri $env:KEYFACTOR_AUTH_TOKEN_URL -Body $tokenBody | |
| } catch { | |
| $statusCode = $null | |
| $responseBody = $null | |
| if ($_.Exception.Response) { | |
| try { | |
| $statusCode = [int]$_.Exception.Response.StatusCode | |
| } catch { | |
| $statusCode = $null | |
| } | |
| try { | |
| $reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream()) | |
| $responseBody = $reader.ReadToEnd() | |
| $reader.Dispose() | |
| } catch { | |
| $responseBody = $null | |
| } | |
| } | |
| $errorMessage = "Failed to fetch OAuth token from $($env:KEYFACTOR_AUTH_TOKEN_URL): $($_.Exception.Message)" | |
| if ($statusCode) { | |
| $errorMessage += " (HTTP $statusCode)" | |
| } | |
| if ($responseBody) { | |
| $errorMessage += ". Response: $responseBody" | |
| } | |
| Write-Error $errorMessage | |
| exit 1 | |
| } | |
| if (-not $tokenResp -or [string]::IsNullOrWhiteSpace($tokenResp.access_token)) { | |
| $tokenErrorDetails = $null | |
| if ($tokenResp) { | |
| $tokenErrorDetails = $tokenResp | ConvertTo-Json -Compress | |
| } | |
| $errorMessage = "OAuth token response did not contain a non-empty access_token." | |
| if ($tokenErrorDetails) { | |
| $errorMessage += " Response: $tokenErrorDetails" | |
| } | |
| Write-Error $errorMessage | |
| exit 1 | |
| } |
| Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -Body $Body -ContentType "application/json" | Out-Null | ||
| Write-Host " OK" | ||
| } catch { | ||
| Write-Warning " FAILED: $($_.Exception.Message)" |
There was a problem hiding this comment.
New-StoreType catches failures and only emits a warning, but then the script continues and ends with "Completed." even if one or more store type creations failed. For automation usage this makes failures easy to miss; consider tracking failures and exiting non-zero at the end (or re-throw / exit 1 on first failure).
| Write-Warning " FAILED: $($_.Exception.Message)" | |
| Write-Warning " FAILED: $($_.Exception.Message)" | |
| throw |
Merge release-1.2 to main - Automated PR