Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/eks-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ jobs:
AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}"

# GitHub OAuth secrets
APP_NAME_GITHUB: "${{ secrets.APP_NAME_GITHUB }}"
CLIENT_ID_GITHUB: "${{ secrets.CLIENT_ID_GITHUB }}"
APP_CLIENT_SECRET: "${{ secrets.APP_CLIENT_SECRET }}"
# GitHub App Configuration
APP_NAME_GITHUB: "watchflow"
APP_CLIENT_ID_GITHUB: "${{ secrets.APP_CLIENT_ID_GITHUB }}"
APP_CLIENT_SECRET_GITHUB: "${{ secrets.APP_CLIENT_SECRET_GITHUB }}"
PRIVATE_KEY_BASE64_GITHUB: "${{ secrets.PRIVATE_KEY_BASE64_GITHUB }}"
WEBHOOK_SECRET_GITHUB: "${{ secrets.WEBHOOK_SECRET_GITHUB }}"

Expand Down Expand Up @@ -95,8 +95,8 @@ jobs:
--set image.repository=${{ env.IMAGE_REPOSITORY }} \
--set image.tag=${{ env.IMAGE_TAG }} \
--set secrets.APP_NAME_GITHUB=${{ env.APP_NAME_GITHUB }} \
--set secrets.CLIENT_ID_GITHUB=${{ env.CLIENT_ID_GITHUB }} \
--set secrets.APP_CLIENT_SECRET=${{ env.APP_CLIENT_SECRET }} \
--set secrets.APP_CLIENT_ID_GITHUB=${{ env.APP_CLIENT_ID_GITHUB }} \
--set secrets.APP_CLIENT_SECRET_GITHUB=${{ env.APP_CLIENT_SECRET_GITHUB }} \
Comment on lines +98 to +99
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's verify the current state of the file at lines 98-99
cat -n .github/workflows/eks-deploy.yaml | sed -n '90,110p'

Repository: warestack/watchflow

Length of output: 1648


🏁 Script executed:

# Run the provided verification commands
rg -n --type=yaml -C2 -- '--set\s+secrets\.[A-Z0-9_]+=' .github/workflows/eks-deploy.yaml
rg -n --type=yaml -- '--set-string\s+secrets\.[A-Z0-9_]+=' .github/workflows/eks-deploy.yaml || true

Repository: warestack/watchflow

Length of output: 996


🌐 Web query:

Helm --set vs --set-string security best practices for secrets

💡 Result:

What’s the difference?

  • --set: Helm parses types (e.g., true→bool, 123→int).
  • --set-string: Same as --set, but forces the value to be a string, preventing unwanted type coercion (useful for things that look like numbers/bools, IDs with leading zeros, etc.). Helm documents this directly: “to force string values, use --set-string”. [1]

Security reality for secrets (best practice)

For secrets, --set vs --set-string is mostly a correctness choice, not a meaningful security improvement:

  • Both put the secret value into Helm’s computed values and (unless you take special measures) it becomes part of Helm’s release state.
  • Helm’s own docs warn that release information includes chart contents and values files and “might contain sensitive data (like passwords, private keys…)” and must be protected via RBAC/encryption-at-rest considerations. [2]
  • Practically, anyone with sufficient access can often retrieve sensitive Helm values (e.g., via helm get values / access to the release Secret), so passing secrets as values at all increases the blast radius. [3]

Recommended approaches (instead of --set/--set-string for secrets)

  1. Prefer referencing an existing Kubernetes Secret (charts often support patterns like existingSecret, secretName, existingSecretKey, etc.), so the secret value isn’t carried around in Helm values/history.
  2. If you must provide secret material as “values”, use encrypted secret values files (GitOps-friendly), e.g. SOPS + helm-secrets so the repository stores only ciphertext and decryption happens at deploy time. [4]
  3. For products/charts that support it, store sensitive parameters in Kubernetes Secrets rather than inline values (vendor guidance commonly recommends this). [5]
  4. If secrets ever land in Helm release state, lock down access to Secrets in that namespace and ensure Kubernetes encryption at rest is enabled (because Helm’s release state can contain sensitive values). [2][3]

If you still use CLI flags

  • Use --set-string for any secret that could be mis-parsed (e.g., 007, true, 1234) to avoid breaking auth due to type coercion. [1]
  • Prefer -f values.yaml over flags for anything sensitive/long-lived, and keep secret files encrypted (SOPS) or out of source control.

Sources

[1] Helm upgrade command docs (flags --set, --set-string, --set-file, etc.) (helm.sh)
[2] Helm “Advanced” topic: release info contains chart contents + values and may include sensitive data; RBAC/Secrets vs ConfigMaps notes (helm.sh)
[3] Google Cloud Community (Apigee Hybrid): sensitive Helm values visible via Helm release Secrets / helm get values; recommends restricting access (googlecloudcommunity.com)
[4] Baeldung: encrypting values with SOPS and using helm-secrets to deploy without exposing secrets in repo (baeldung.com)
[5] GitGuardian Helm install docs: favor Kubernetes Secrets over inline sensitive parameters (docs.gitguardian.com)

Citations:


Change all secret values to use --set-string to prevent type coercion.

Lines 97–104 pass multiple secrets using --set, which allows Helm to interpret values as booleans, numbers, or other types. If a secret value resembles a boolean (true, false) or number, Helm may reinterpret it, breaking authentication at deploy time. Use --set-string with quoted expansions instead.

Proposed fix
-            --set secrets.APP_NAME_GITHUB=${{ env.APP_NAME_GITHUB }} \
-            --set secrets.APP_CLIENT_ID_GITHUB=${{ env.APP_CLIENT_ID_GITHUB }} \
-            --set secrets.APP_CLIENT_SECRET_GITHUB=${{ env.APP_CLIENT_SECRET_GITHUB }} \
-            --set secrets.PRIVATE_KEY_BASE64_GITHUB=${{ env.PRIVATE_KEY_BASE64_GITHUB }} \
-            --set secrets.WEBHOOK_SECRET_GITHUB=${{ env.WEBHOOK_SECRET_GITHUB }} \
-            --set secrets.OPENAI_API_KEY=${{ env.OPENAI_API_KEY }} \
-            --set secrets.LANGCHAIN_API_KEY=${{ env.LANGCHAIN_API_KEY }} \
-            --set secrets.LANGCHAIN_PROJECT=${{ env.LANGCHAIN_PROJECT }} \
+            --set-string secrets.APP_NAME_GITHUB="${{ env.APP_NAME_GITHUB }}" \
+            --set-string secrets.APP_CLIENT_ID_GITHUB="${{ env.APP_CLIENT_ID_GITHUB }}" \
+            --set-string secrets.APP_CLIENT_SECRET_GITHUB="${{ env.APP_CLIENT_SECRET_GITHUB }}" \
+            --set-string secrets.PRIVATE_KEY_BASE64_GITHUB="${{ env.PRIVATE_KEY_BASE64_GITHUB }}" \
+            --set-string secrets.WEBHOOK_SECRET_GITHUB="${{ env.WEBHOOK_SECRET_GITHUB }}" \
+            --set-string secrets.OPENAI_API_KEY="${{ env.OPENAI_API_KEY }}" \
+            --set-string secrets.LANGCHAIN_API_KEY="${{ env.LANGCHAIN_API_KEY }}" \
+            --set-string secrets.LANGCHAIN_PROJECT="${{ env.LANGCHAIN_PROJECT }}" \
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/eks-deploy.yaml around lines 98 - 99, Replace all Helm
flag usages that pass secrets with --set (e.g., --set
secrets.APP_CLIENT_ID_GITHUB, --set secrets.APP_CLIENT_SECRET_GITHUB and other
secrets.* occurrences) with --set-string and quote the expansion so values are
not type-coerced by Helm; update each flag invocation (the occurrences of --set
secrets.<KEY>) to use --set-string 'secrets.<KEY>=${{ env.<ENV_VAR> }}' style
quoting for every secret passed in this Helm command.

--set secrets.PRIVATE_KEY_BASE64_GITHUB=${{ env.PRIVATE_KEY_BASE64_GITHUB }} \
--set secrets.WEBHOOK_SECRET_GITHUB=${{ env.WEBHOOK_SECRET_GITHUB }} \
--set secrets.OPENAI_API_KEY=${{ env.OPENAI_API_KEY }} \
Expand Down
6 changes: 3 additions & 3 deletions helm-chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ affinity: {}

# Application secrets configuration - only the required ones (no defaults in config.py)
secrets:
APP_NAME_GITHUB: ""
CLIENT_ID_GITHUB: ""
APP_CLIENT_SECRET: ""
APP_NAME_GITHUB: "watchflow"
APP_CLIENT_ID_GITHUB: ""
APP_CLIENT_SECRET_GITHUB: ""
PRIVATE_KEY_BASE64_GITHUB: ""
WEBHOOK_SECRET_GITHUB: ""
OPENAI_API_KEY: ""
Expand Down
4 changes: 2 additions & 2 deletions src/core/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ def validate(self) -> bool:
errors.append("APP_NAME_GITHUB is required")

if not self.github.app_id:
errors.append("CLIENT_ID_GITHUB is required")
errors.append("APP_CLIENT_ID_GITHUB is required")

if not self.github.app_client_secret:
errors.append("APP_CLIENT_SECRET is required")
errors.append("APP_CLIENT_SECRET_GITHUB is required")

if not self.github.private_key:
errors.append("PRIVATE_KEY_BASE64_GITHUB is required")
Expand Down