Conversation
feat: add QOVERY_TELEMETRY env var to avoid sending events to Qovery telemetry system
Feat/eng 1883/deployment queue
fix: try to fix aur package release
…ent_queue Feat/eng 1883/deploy with deployment queue
…ent_queue feat: update deploy commands with deployment queue
Feat/deployment action update
…endor_variants feat: enable cloud vendor field on cluster creation
…-fae385 fix: sort projects, environments, and services alphabetically in context set
Lets agents and users retrieve the list of organizations the authenticated token has access to via `qovery organization list`.
feat: add organization list command
Scripts and agents had no safe way to check authentication: 'auth token' requires --print or --json, both of which expose the raw secret. 'auth status' reports authenticated/expiry/org/user without ever including the token, and always re-verifies against the API so a revoked or expired token (including one passed via QOVERY_CLI_ACCESS_TOKEN / Q_CLI_ACCESS_TOKEN, which GetAccessToken previously trusted without a server round trip) is correctly reported as not authenticated.
feat: add qovery auth status command with live token validation
Fetches and prints the Qovery OpenAPI spec (from Qovery/qovery-openapi-spec, the only stable source now that api-doc.qovery.com redirects to rendered docs instead of serving the raw file). Lets scripts and agents discover valid endpoints, methods, and request/response shapes before calling 'qovery api <endpoint>', instead of guessing or trusting a possibly stale copy. Requires no authentication. Added as a subcommand of the existing 'api' command rather than a new top-level command or a --spec flag, matching the parent-command-with-subcommands shape already used by 'auth'/'context'.
…t path Addresses Copilot review feedback on #691: the "written to" status message was going to stdout via PrintlnInfo, defeating the point of -o (a clean stream to script against); the direct os.Stdout.Write also discarded its error, so a broken pipe or full disk still exited 0.
Ticket: QOV-2104
…spec.go That pattern only guards against SA5011 false positives where staticcheck can't prove code after os.Exit is unreachable and flags a later dereference as a possible nil access. None of the five os.Exit(1) calls here are followed by code that dereferences anything from the failed call, so there was nothing for staticcheck to misjudge. Verified by removing all five and confirming staticcheck, go vet, and go build stay clean.
…_status.go Same issue as flagged on cmd/api_spec.go: this pattern only guards against a real SA5011 false positive, and none of the three os.Exit(1) calls here are followed by code that dereferences anything from the failed call. Verified by removing all three and confirming staticcheck, go vet, and go build stay clean.
feat: add qovery api spec command
Avoid expanding an empty engine_image_overrides array under set -u, which causes qovery demo up to files on macOS’s Bash 3.2 with an “unbound variable” error. Add a regression test for the nounset-safe expansion.
fix: remove unnecessary panic("unreachable") after os.Exit(1) in auth_status.go
GetAccessToken() calls ListOrganization() to validate the token, then rejects any qovery api call outright when the org list is empty — even when the call being made is the org-creation call itself. This makes the documented `qovery api organization --field name=... --field plan=...` example (shown in `qovery api --help`) impossible to run for a brand-new account with zero organizations, forcing a detour through the web console just to bootstrap the very first org. Add GetAccessTokenAllowNoOrg(), used only by runAPI when the request is exactly `POST /organization`, to skip that guard for this one legitimate bootstrap case. Every other `qovery api` call keeps the existing protection against operating with no organization to scope to.
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
- Per review feedback, drop the GetAccessToken()/GetAccessTokenAllowNoOrg() wrapper pair and export getAccessToken directly as GetAccessToken(skipOrgaCheck bool). Every existing call site is updated to pass false; only runAPI's org-creation path passes true. - Add utils/context_test.go: unit tests for checkOrgaValid, plus an httptest-backed end-to-end test proving GetAccessToken(false) still rejects a zero-organization account for every other command while GetAccessToken(true) allows it through for org creation only.
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
…orgs Allow creating the first organization via qovery api organization
Why: The v1.168.5 release container job failed at `RUN go mod download` with a transient HTTP/2 stream reset from proxy.golang.org. The Docker build fetched all 198 modules uncached with no retry, so a single reset broke the release and left no image published for the tag. What: - Retry `go mod download` up to 5 times with linear backoff (5/10/15/20s). - Mount BuildKit caches for the module cache and the Go build cache. Notes: GOPROXY's `direct` fallback does not cover this error class (only 404/410), so a retry is the only way to absorb it.
Why: The retry loop retried every failure, including ones decidable from go.mod and go.sum alone. A go.sum checksum mismatch can never succeed on retry, so the build burned 5 download passes and ~50s of backoff before failing, and buried Go's SECURITY ERROR banner in retry noise. What: Capture stderr and short-circuit when it matches a non-transient error class (checksum mismatch, missing go.sum entry, go.mod parse or version errors). Everything else stays retried. Notes: Exit codes cannot discriminate here: network failures, checksum mismatches and unknown revisions all exit 1, and -json changes neither the exit code nor the stderr routing, so stderr matching is the only available signal. The match list is a denylist rather than an allowlist of retryable errors on purpose: an unmatched permanent error costs ~50s of CI, while an unmatched transient one would break a release.
Why: The fail-fast list missed deterministic errors, so a bad module path or an unresolvable version still burned 5 download passes and ~50s of backoff. The list was drawn on the wrong axis: local-vs-remote rather than whether the proxy actually answered. `unknown revision` is a definitive negative answer, not a failed round trip, so retrying it cannot help. What: Retry only transport failures. Add `unknown revision`, `malformed module path` and `module lookup disabled` to the non-transient set. Notes: Kept as a denylist rather than an allowlist of retryable errors. The failure that motivated this PR was `stream error: stream ID 171; INTERNAL_ERROR`, a string no hand-written transient allowlist would plausibly have contained; missing an entry there breaks a release, while missing one here costs ~50s. `no matching versions for query` is not reachable from `go mod download` with pinned versions, which report `errors parsing go.mod` or `unknown revision`.
…ad-retry fix(docker): retry go mod download and cache Go modules
There was a problem hiding this comment.
40 issues found across 422 files
Confidence score: 1/5
cmd/admin_cluster_update_kubeconfig.goaccepts the complete kubeconfig through--kubeconfig, exposing cluster credentials in shell history and process listings; read it from protected stdin or a file instead.cmd/database_list.goemits master logins and passwords in--jsonoutput even without--show-credentials, risking credential disclosure; apply the existing masking and explicit opt-in behavior to JSON output.- Secret-capable flags in
cmd/environment_env_create.go,cmd/environment_env_update.go,cmd/cronjob_env_override_create.go,cmd/container_env_update.go,cmd/cronjob_env_create.go,cmd/application_env_create.go, andcmd/container_env_create.goexpose values through shell history and process listings; use protected stdin, hidden prompts, or protected files instead. cmd/admin_encrypt_secret.goaccepts cleartext through--message, exposing it in shell history and process listings; read the value from a hidden prompt or stdin.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="cmd/admin_cluster_update_kubeconfig.go">
<violation number="1" location="cmd/admin_cluster_update_kubeconfig.go:26">
P0: Passing the complete kubeconfig through `--kubeconfig` exposes cluster credentials in shell history and process listings. Read it from protected stdin or a file instead of accepting the secret as a command-line argument.</violation>
</file>
<file name="cmd/database_list.go">
<violation number="1" location="cmd/database_list.go:115">
P0: When users run `database list --json` without `--show-credentials`, the command still writes each database's master login and password to stdout. Apply the same masking/explicit opt-in used by the table output to both JSON fields.</violation>
</file>
<file name="cmd/environment_env_create.go">
<violation number="1" location="cmd/environment_env_create.go:57">
P1: When `--secret` is used, the secret value is supplied as a command-line argument, where process listings and shell history can expose it. Read secret values from a protected prompt or stdin instead, and do not require `--value` for that mode.</violation>
<violation number="2" location="cmd/environment_env_create.go:57">
P1: When creating a secret, `--value` puts the secret in the process command line. Read secret values from stdin or an interactive prompt instead of accepting them as a command-line argument.</violation>
</file>
<file name="cmd/environment_env_update.go">
<violation number="1" location="cmd/environment_env_update.go:57">
P1: When updating a secret, `--value` exposes the secret in shell history and process listings. Read the value from a masked prompt or stdin instead of accepting the secret as a command-line flag.</violation>
<violation number="2" location="cmd/environment_env_update.go:57">
P1: When updating a secret, `--value` puts the new secret in the process command line. Read secret values from stdin or an interactive prompt instead of accepting them as a command-line argument.</violation>
</file>
<file name="cmd/cronjob_env_override_create.go">
<violation number="1" location="cmd/cronjob_env_override_create.go:72">
P1: When `--value` contains a secret, the secret is exposed in the process arguments and commonly shell history. Do not accept secret-capable values as a command-line flag; read the value from protected stdin or a hidden prompt instead.</violation>
</file>
<file name="cmd/container_env_update.go">
<violation number="1" location="cmd/container_env_update.go:72">
P1: When updating a secret, `--value` exposes the secret through the process command line and shell history. Read the value from hidden interactive input or stdin instead of accepting sensitive data as a command-line argument.</violation>
</file>
<file name="cmd/cronjob_env_create.go">
<violation number="1" location="cmd/cronjob_env_create.go:72">
P1: When `--secret` is used, this command requires the secret in `--value`, exposing it to shell history and local process inspection. Read secret values from stdin or an interactive no-echo prompt instead of accepting them as a command-line argument.</violation>
</file>
<file name="cmd/application_env_create.go">
<violation number="1" location="cmd/application_env_create.go:72">
P1: When `--secret` is used, this flag requires the secret plaintext as a command-line argument, exposing it through process inspection and shell history. Accept secret values through hidden interactive input or stdin instead of requiring `--value` for secrets.</violation>
</file>
<file name="cmd/container_env_create.go">
<violation number="1" location="cmd/container_env_create.go:72">
P1: When `--secret` is used, this required `--value` flag exposes the secret through shell history and process listings. Read secret values from stdin, an interactive prompt, or a protected file instead of requiring them as command-line arguments.</violation>
<violation number="2" location="cmd/container_env_create.go:72">
P1: When a secret is supplied with `--value`, the shell exposes it as a command-line argument. Read secret values from stdin or another non-argv input instead of accepting them directly in `--value`.</violation>
</file>
<file name="cmd/admin_encrypt_secret.go">
<violation number="1" location="cmd/admin_encrypt_secret.go:30">
P1: Passing the cleartext through `--message` exposes it in shell history and process listings. Read the value from a hidden prompt or stdin instead of a command-line flag.</violation>
<violation number="2" location="cmd/admin_encrypt_secret.go:50">
P1: The successful path writes `messageToEncrypt` (the cleartext secret) to stdout, so terminal capture or redirected logs retain the secret. Print only the encrypted result.</violation>
</file>
<file name="cmd/container_env_override_create.go">
<violation number="1" location="cmd/container_env_override_create.go:72">
P1: When creating a container override, `--value` puts the secret in the process arguments, where shell history and process monitoring can expose it. Read the override value from stdin or a no-echo prompt instead of requiring a secret-bearing command-line flag.</violation>
</file>
<file name="cmd/cronjob_env_update.go">
<violation number="1" location="cmd/cronjob_env_update.go:72">
P1: When `--value` contains a secret, the plaintext is stored in shell history and exposed through process arguments. Read secret values from a hidden prompt or stdin instead of a command-line flag.</violation>
</file>
<file name="cmd/application_env_update.go">
<violation number="1" location="cmd/application_env_update.go:72">
P1: When this command updates a secret, `--value` exposes the plaintext through the process arguments and shell history. Read secret values from a hidden prompt or stdin instead of requiring them as command-line arguments.</violation>
</file>
<file name="cmd/environment_env_override_create.go">
<violation number="1" location="cmd/environment_env_override_create.go:58">
P1: When creating a secret override, `--value` puts the secret in the process command line. Read secret values from stdin or an interactive prompt instead of accepting them as a command-line argument.</violation>
<violation number="2" location="cmd/environment_env_override_create.go:58">
P1: Because `--value` is a plain string flag, secret contents are exposed through shell history and process listings. Accept secret values through a non-echoing prompt or stdin/file descriptor instead of a command-line flag.</violation>
</file>
<file name="cmd/admin_load_credentials.go">
<violation number="1" location="cmd/admin_load_credentials.go:22">
P1: This command persists kubeconfig and, for GCP clusters, raw credential files under a predictable `/tmp` path with requested mode `0777`, without cleanup after the shell exits. Use private `0600` storage and remove the files when the child shell terminates.</violation>
</file>
<file name="cmd/admin_k9s.go">
<violation number="1" location="cmd/admin_k9s.go:50">
P1: This stores the cluster kubeconfig in a world-readable temporary file because `WriteInFile` uses mode `0777`; another local user can read the credentials while k9s is running. Write the kubeconfig with `0600` in a private directory, or update the helper to use secure permissions.</violation>
<violation number="2" location="cmd/admin_k9s.go:80">
P1: When `--no-bastion` is used without `BASTION_ADDR`, `checkEnv` exits before the flag can skip `SetBastionConnection`, so direct connections cannot work. Require `BASTION_ADDR` only when `!doNotConnectToBastion`.</violation>
</file>
<file name="cmd/container_env_list.go">
<violation number="1" location="cmd/container_env_list.go:74">
P1: When the API returns a secret value, `container env list --json` prints it because `GetEnvVarJsonOutput` ignores `IsSecret` and `--show-values`. Mask secret values in the JSON serializer or require an explicit, safe opt-in before emitting them.</violation>
</file>
<file name="cmd/admin_notify_users_cluster_failure.go">
<violation number="1" location="cmd/admin_notify_users_cluster_failure.go:34">
P1: When `--cluster-id` is omitted, this always passes `&clusterId`, so `NotifyUsersClusterFailure` selects the single-cluster path with an empty ID instead of notifying admins of all failed clusters. Pass `nil` when `clusterId` is empty.</violation>
</file>
<file name=".github/dependabot.yml">
<violation number="1" location=".github/dependabot.yml:12">
P1: Dependabot does not recognize `update_types` as a group option, so this configuration can fail validation and prevent dependency update automation. Rename the key to `update-types` and specify the supported update types, or omit it to include all update types.</violation>
</file>
<file name="cmd/enterprise_connection_update.go">
<violation number="1" location="cmd/enterprise_connection_update.go:47">
P1: When an existing connection has group sync enabled, updating only `--default-role` disables it. Preserve `existingConnection.EnforceGroupSync` unless the flag was explicitly supplied, including explicit `false`.</violation>
</file>
<file name="cmd/cronjob_external_secret_update.go">
<violation number="1" location="cmd/cronjob_external_secret_update.go:53">
P1: When a user updates only `--reference`, this lookup searches for an empty secret-manager access name and exits before `UpdateServiceExternalSecret`. Resolve the access only when `--secret-manager-access-name` is provided.</violation>
</file>
<file name="cmd/application_external_secret_update.go">
<violation number="1" location="cmd/application_external_secret_update.go:53">
P1: When users update only the reference with `--reference`, this unconditional lookup searches for an empty-named secret-manager access and aborts before the update. Resolve the access ID only when `--secret-manager-access-name` is supplied.</violation>
</file>
<file name="cmd/admin_deploy_failed_force_internal_error.go">
<violation number="1" location="cmd/admin_deploy_failed_force_internal_error.go:23">
P1: When `-d` is below five minutes or contains seconds, this call still reaches the force endpoint. The helper only logs values under five minutes and truncates durations to whole minutes, so deployments can be forced earlier than requested; validate this range before invoking it.</violation>
</file>
<file name="cmd/environment_external_secret_update.go">
<violation number="1" location="cmd/environment_external_secret_update.go:50">
P1: When updating only the external-secret reference, this unconditional lookup searches for an empty secret-manager-access name and exits before the update. Resolve the access ID only when `--secret-manager-access-name` is provided.</violation>
</file>
<file name="cmd/cluster_upgrade_to_next_kubernetes_version.go">
<violation number="1" location="cmd/cluster_upgrade_to_next_kubernetes_version.go:122">
P1: With `--watch`, error states count as terminal, but this unconditional message reports the upgrade as successful and returns exit code zero. Distinguish successful terminal states from terminal errors and fail on errors.</violation>
</file>
<file name="cmd/application_env_override_create.go">
<violation number="1" location="cmd/application_env_override_create.go:72">
P1: When overriding a secret, this required flag puts the plaintext in shell history, `ps`/`/proc`, CI logs, and command auditing. Read secret values from masked stdin or a no-echo prompt instead of requiring them as command-line arguments.</violation>
</file>
<file name="cmd/application_external_secret_delete.go">
<violation number="1" location="cmd/application_external_secret_delete.go:53">
P1: When the secret was created with `--scope PROJECT` or `--scope ENVIRONMENT`, this call searches only application-scoped variables, so deletion reports “not found” and leaves the secret behind. Carry the selected scope and matching parent ID through deletion, or reject non-`APPLICATION` scopes for this command.</violation>
</file>
<file name="cmd/auth_token.go">
<violation number="1" location="cmd/auth_token.go:55">
P1: When a freshly authenticated account has no organizations, this call returns `you don't have any organization` before printing the token. That prevents the documented direct-API workflow from retrieving the token needed to create the first organization; use the no-organization-check path for this token-export command.</violation>
</file>
<file name="cmd/admin_jwt_delete.go">
<violation number="1" location="cmd/admin_jwt_delete.go:46">
P1: When the DELETE request cannot be sent, this status check dereferences a nil response and panics before reporting the transport error. Check `err` before reading `res.StatusCode`.</violation>
</file>
<file name="cmd/demo_scripts/destroy_qovery_demo.sh">
<violation number="1" location="cmd/demo_scripts/destroy_qovery_demo.sh:30">
P1: When Qovery deletion fails, `|| true` makes `demo destroy --delete-qovery-config` report success while the remote configuration remains. Let this request fail the script so the user can retry.</violation>
</file>
<file name="cmd/database_cancel.go">
<violation number="1" location="cmd/database_cancel.go:14">
P1: `qovery database cancel` never cancels a deployment; it only prints a suggestion and exits. Resolve the selected database and call `utils.CancelServiceDeployment` with `utils.DatabaseType` (or remove this command until it can perform the operation).</violation>
</file>
<file name="cmd/cronjob_env_list.go">
<violation number="1" location="cmd/cronjob_env_list.go:74">
P1: When `--json` is used, this path can expose cronjob secret values because `GetEnvVarJsonOutput` serializes `Value` and `interpolated_value` without applying the table formatter's `IsSecret` mask. Make the JSON formatter redact secrets, regardless of the output mode.</violation>
</file>
<file name="cmd/cluster_lock.go">
<violation number="1" location="cmd/cluster_lock.go:57">
P1: When the lock request fails before receiving an HTTP response, `http` is nil and this dereference panics, masking the request error. Check `http` and `http.Body` before reading the response.</violation>
</file>
<file name="cmd/application_env_list.go">
<violation number="1" location="cmd/application_env_list.go:74">
P1: When `--json` is selected, this path bypasses the table masking and exposes environment-variable values regardless of `--show-values`; it can also expose secrets if the API returns their values. Redact secret values and honor `--show-values` before generating JSON.</violation>
</file>
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.
Re-trigger cubic
| func init() { | ||
| adminClusterUpdateKubeconfigCmd.Flags().StringVar(&organizationId, "organization-id", "", "The cluster's organization ") | ||
| adminClusterUpdateKubeconfigCmd.Flags().StringVar(&clusterId, "cluster-id", "", "The cluster id to target") | ||
| adminClusterUpdateKubeconfigCmd.Flags().StringVar(&clusterKubeconfig, "kubeconfig", "", "The cluster kubeconfig string value") |
There was a problem hiding this comment.
P0: Passing the complete kubeconfig through --kubeconfig exposes cluster credentials in shell history and process listings. Read it from protected stdin or a file instead of accepting the secret as a command-line argument.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/admin_cluster_update_kubeconfig.go, line 26:
<comment>Passing the complete kubeconfig through `--kubeconfig` exposes cluster credentials in shell history and process listings. Read it from protected stdin or a file instead of accepting the secret as a command-line argument.</comment>
<file context>
@@ -0,0 +1,60 @@
+func init() {
+ adminClusterUpdateKubeconfigCmd.Flags().StringVar(&organizationId, "organization-id", "", "The cluster's organization ")
+ adminClusterUpdateKubeconfigCmd.Flags().StringVar(&clusterId, "cluster-id", "", "The cluster id to target")
+ adminClusterUpdateKubeconfigCmd.Flags().StringVar(&clusterKubeconfig, "kubeconfig", "", "The cluster kubeconfig string value")
+ adminClusterCmd.AddCommand(adminClusterUpdateKubeconfigCmd)
+}
</file context>
| "login": res.Login, | ||
| "password": res.Password, |
There was a problem hiding this comment.
P0: When users run database list --json without --show-credentials, the command still writes each database's master login and password to stdout. Apply the same masking/explicit opt-in used by the table output to both JSON fields.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/database_list.go, line 115:
<comment>When users run `database list --json` without `--show-credentials`, the command still writes each database's master login and password to stdout. Apply the same masking/explicit opt-in used by the table output to both JSON fields.</comment>
<file context>
@@ -0,0 +1,138 @@
+ "status": utils.FindStatus(statuses, database.Id),
+ "host": database.Host,
+ "port": res.Port,
+ "login": res.Login,
+ "password": res.Password,
+ })
</file context>
| "login": res.Login, | |
| "password": res.Password, | |
| "login": func() string { if showCredentials { return res.Login }; return "********" }(), | |
| "password": func() string { if showCredentials { return res.Password }; return "********" }(), |
| environmentEnvCreateCmd.Flags().StringVarP(&projectName, "project", "", "", "Project Name") | ||
| environmentEnvCreateCmd.Flags().StringVarP(&environmentName, "environment", "", "", "Environment Name") | ||
| environmentEnvCreateCmd.Flags().StringVarP(&utils.Key, "key", "k", "", "Environment variable or secret key") | ||
| environmentEnvCreateCmd.Flags().StringVarP(&utils.Value, "value", "v", "", "Environment variable or secret value") |
There was a problem hiding this comment.
P1: When --secret is used, the secret value is supplied as a command-line argument, where process listings and shell history can expose it. Read secret values from a protected prompt or stdin instead, and do not require --value for that mode.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/environment_env_create.go, line 57:
<comment>When `--secret` is used, the secret value is supplied as a command-line argument, where process listings and shell history can expose it. Read secret values from a protected prompt or stdin instead, and do not require `--value` for that mode.</comment>
<file context>
@@ -0,0 +1,65 @@
+ environmentEnvCreateCmd.Flags().StringVarP(&projectName, "project", "", "", "Project Name")
+ environmentEnvCreateCmd.Flags().StringVarP(&environmentName, "environment", "", "", "Environment Name")
+ environmentEnvCreateCmd.Flags().StringVarP(&utils.Key, "key", "k", "", "Environment variable or secret key")
+ environmentEnvCreateCmd.Flags().StringVarP(&utils.Value, "value", "v", "", "Environment variable or secret value")
+ environmentEnvCreateCmd.Flags().StringVarP(&utils.EnvironmentScope, "scope", "", "ENVIRONMENT", "Scope of this env var <PROJECT|ENVIRONMENT>")
+ environmentEnvCreateCmd.Flags().BoolVarP(&utils.IsSecret, "secret", "", false, "This environment variable is a secret")
</file context>
| environmentEnvUpdateCmd.Flags().StringVarP(&projectName, "project", "", "", "Project Name") | ||
| environmentEnvUpdateCmd.Flags().StringVarP(&environmentName, "environment", "", "", "Environment Name") | ||
| environmentEnvUpdateCmd.Flags().StringVarP(&utils.Key, "key", "k", "", "Environment variable or secret key") | ||
| environmentEnvUpdateCmd.Flags().StringVarP(&utils.Value, "value", "v", "", "Environment variable or secret value") |
There was a problem hiding this comment.
P1: When updating a secret, --value exposes the secret in shell history and process listings. Read the value from a masked prompt or stdin instead of accepting the secret as a command-line flag.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/environment_env_update.go, line 57:
<comment>When updating a secret, `--value` exposes the secret in shell history and process listings. Read the value from a masked prompt or stdin instead of accepting the secret as a command-line flag.</comment>
<file context>
@@ -0,0 +1,63 @@
+ environmentEnvUpdateCmd.Flags().StringVarP(&projectName, "project", "", "", "Project Name")
+ environmentEnvUpdateCmd.Flags().StringVarP(&environmentName, "environment", "", "", "Environment Name")
+ environmentEnvUpdateCmd.Flags().StringVarP(&utils.Key, "key", "k", "", "Environment variable or secret key")
+ environmentEnvUpdateCmd.Flags().StringVarP(&utils.Value, "value", "v", "", "Environment variable or secret value")
+
+ _ = environmentEnvUpdateCmd.MarkFlagRequired("project")
</file context>
| cronjobEnvOverrideCreateCmd.Flags().StringVarP(&environmentName, "environment", "", "", "Environment Name") | ||
| cronjobEnvOverrideCreateCmd.Flags().StringVarP(&cronjobName, "cronjob", "n", "", "Cronjob Name") | ||
| cronjobEnvOverrideCreateCmd.Flags().StringVarP(&utils.Key, "key", "k", "", "Environment variable or secret key") | ||
| cronjobEnvOverrideCreateCmd.Flags().StringVarP(&utils.Value, "value", "", "", "Environment variable or secret value") |
There was a problem hiding this comment.
P1: When --value contains a secret, the secret is exposed in the process arguments and commonly shell history. Do not accept secret-capable values as a command-line flag; read the value from protected stdin or a hidden prompt instead.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/cronjob_env_override_create.go, line 72:
<comment>When `--value` contains a secret, the secret is exposed in the process arguments and commonly shell history. Do not accept secret-capable values as a command-line flag; read the value from protected stdin or a hidden prompt instead.</comment>
<file context>
@@ -0,0 +1,78 @@
+ cronjobEnvOverrideCreateCmd.Flags().StringVarP(&environmentName, "environment", "", "", "Environment Name")
+ cronjobEnvOverrideCreateCmd.Flags().StringVarP(&cronjobName, "cronjob", "n", "", "Cronjob Name")
+ cronjobEnvOverrideCreateCmd.Flags().StringVarP(&utils.Key, "key", "k", "", "Environment variable or secret key")
+ cronjobEnvOverrideCreateCmd.Flags().StringVarP(&utils.Value, "value", "", "", "Environment variable or secret value")
+ cronjobEnvOverrideCreateCmd.Flags().StringVarP(&utils.JobScope, "scope", "", "JOB", "Scope of this alias <PROJECT|ENVIRONMENT|JOB>")
+
</file context>
| clusterId=$(curl -s -X GET --fail-with-body -H "@${AUTHORIZATION_HEADER_FILE}" -H 'Content-Type: application/json' ${QOVERY_API_URL}/organization/"${ORGANIZATION_ID}"/cluster | jq -r '.results[] | select(.name=="'"$clusterName"'") | .id') | ||
|
|
||
| if [ -n "$clusterId" ]; then | ||
| curl -s -X DELETE --fail-with-body -H "@${AUTHORIZATION_HEADER_FILE}" ${QOVERY_API_URL}'/organization/'"${ORGANIZATION_ID}"'/cluster/'"${clusterId}"'?deleteMode=DELETE_QOVERY_CONFIG' || true |
There was a problem hiding this comment.
P1: When Qovery deletion fails, || true makes demo destroy --delete-qovery-config report success while the remote configuration remains. Let this request fail the script so the user can retry.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/demo_scripts/destroy_qovery_demo.sh, line 30:
<comment>When Qovery deletion fails, `|| true` makes `demo destroy --delete-qovery-config` report success while the remote configuration remains. Let this request fail the script so the user can retry.</comment>
<file context>
@@ -0,0 +1,95 @@
+ clusterId=$(curl -s -X GET --fail-with-body -H "@${AUTHORIZATION_HEADER_FILE}" -H 'Content-Type: application/json' ${QOVERY_API_URL}/organization/"${ORGANIZATION_ID}"/cluster | jq -r '.results[] | select(.name=="'"$clusterName"'") | .id')
+
+ if [ -n "$clusterId" ]; then
+ curl -s -X DELETE --fail-with-body -H "@${AUTHORIZATION_HEADER_FILE}" ${QOVERY_API_URL}'/organization/'"${ORGANIZATION_ID}"'/cluster/'"${clusterId}"'?deleteMode=DELETE_QOVERY_CONFIG' || true
+ fi
+}
</file context>
| curl -s -X DELETE --fail-with-body -H "@${AUTHORIZATION_HEADER_FILE}" ${QOVERY_API_URL}'/organization/'"${ORGANIZATION_ID}"'/cluster/'"${clusterId}"'?deleteMode=DELETE_QOVERY_CONFIG' || true | |
| curl -s -X DELETE --fail-with-body -H "@${AUTHORIZATION_HEADER_FILE}" "${QOVERY_API_URL}/organization/${ORGANIZATION_ID}/cluster/${clusterId}?deleteMode=DELETE_QOVERY_CONFIG" |
| Run: func(cmd *cobra.Command, args []string) { | ||
| utils.Capture(cmd) | ||
|
|
||
| utils.PrintlnInfo("Use: 'qovery environment cancel' to cancel this deployment") |
There was a problem hiding this comment.
P1: qovery database cancel never cancels a deployment; it only prints a suggestion and exits. Resolve the selected database and call utils.CancelServiceDeployment with utils.DatabaseType (or remove this command until it can perform the operation).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/database_cancel.go, line 14:
<comment>`qovery database cancel` never cancels a deployment; it only prints a suggestion and exits. Resolve the selected database and call `utils.CancelServiceDeployment` with `utils.DatabaseType` (or remove this command until it can perform the operation).</comment>
<file context>
@@ -0,0 +1,20 @@
+ Run: func(cmd *cobra.Command, args []string) {
+ utils.Capture(cmd)
+
+ utils.PrintlnInfo("Use: 'qovery environment cancel' to cancel this deployment")
+ },
+}
</file context>
| } | ||
|
|
||
| if jsonFlag { | ||
| utils.Println(utils.GetEnvVarJsonOutput(variables, utils.SortKeys)) |
There was a problem hiding this comment.
P1: When --json is used, this path can expose cronjob secret values because GetEnvVarJsonOutput serializes Value and interpolated_value without applying the table formatter's IsSecret mask. Make the JSON formatter redact secrets, regardless of the output mode.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/cronjob_env_list.go, line 74:
<comment>When `--json` is used, this path can expose cronjob secret values because `GetEnvVarJsonOutput` serializes `Value` and `interpolated_value` without applying the table formatter's `IsSecret` mask. Make the JSON formatter redact secrets, regardless of the output mode.</comment>
<file context>
@@ -0,0 +1,100 @@
+ }
+
+ if jsonFlag {
+ utils.Println(utils.GetEnvVarJsonOutput(variables, utils.SortKeys))
+ return
+ }
</file context>
| _, http, err := client.ClustersAPI.LockCluster(context.Background(), clusterId).ClusterLockRequest(lockClusterRequest).Execute() | ||
| if err != nil { | ||
| utils.PrintlnError(err) | ||
| result, _ := io.ReadAll(http.Body) |
There was a problem hiding this comment.
P1: When the lock request fails before receiving an HTTP response, http is nil and this dereference panics, masking the request error. Check http and http.Body before reading the response.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/cluster_lock.go, line 57:
<comment>When the lock request fails before receiving an HTTP response, `http` is nil and this dereference panics, masking the request error. Check `http` and `http.Body` before reading the response.</comment>
<file context>
@@ -0,0 +1,80 @@
+ _, http, err := client.ClustersAPI.LockCluster(context.Background(), clusterId).ClusterLockRequest(lockClusterRequest).Execute()
+ if err != nil {
+ utils.PrintlnError(err)
+ result, _ := io.ReadAll(http.Body)
+ LogDetail(result)
+ os.Exit(1)
</file context>
| } | ||
|
|
||
| if jsonFlag { | ||
| utils.Println(utils.GetEnvVarJsonOutput(variables, utils.SortKeys)) |
There was a problem hiding this comment.
P1: When --json is selected, this path bypasses the table masking and exposes environment-variable values regardless of --show-values; it can also expose secrets if the API returns their values. Redact secret values and honor --show-values before generating JSON.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/application_env_list.go, line 74:
<comment>When `--json` is selected, this path bypasses the table masking and exposes environment-variable values regardless of `--show-values`; it can also expose secrets if the API returns their values. Redact secret values and honor `--show-values` before generating JSON.</comment>
<file context>
@@ -0,0 +1,100 @@
+ }
+
+ if jsonFlag {
+ utils.Println(utils.GetEnvVarJsonOutput(variables, utils.SortKeys))
+ return
+ }
</file context>
Summary by cubic
Fixes Synacktiv V-04 (QOV-2260): CLI no longer prints tokens/secrets to stdout and tightens context file permissions. The branch also carries a large backlog of unrelated features, admin tooling, and CI changes.
Migration
qovery auth tokenprints nothing by default; use--printor--json.qovery auth statusnever exposes the token, even with--json.admin deploy,deploy_all,lock,unlock; useadmin cluster deployand related subcommands.qovery admin k9sis read-only by default; opt in with--read-write, skip bastion with--no-bastion.New Features
qovery api <endpoint>for authenticated API calls andapi specto fetch the OpenAPI spec.QOVERY_TELEMETRYenv var to disable telemetry,list-commands, and a terraform backend setup command.release-latestworkflow, CodeQL, Dependabot grouping,Dockerfile, andmise.toml; release builds use Go 1.25 and GoReleaser v2.Written for commit 290cb21. Summary will update on new commits.