Skip to content

fix: bind kept stored secrets to their saved endpoints - #29

Merged
agentHits merged 1 commit into
AgentHits-Devfrom
fix/stored-secret-endpoint-binding
Oct 7, 2026
Merged

agentHits merged 1 commit into
AgentHits-Devfrom
fix/stored-secret-endpoint-binding

Conversation

@agentHits

Copy link
Copy Markdown
Collaborator

What is this PR about?

A stored secret could be reused with a destination chosen by the caller. Update and test procedures refill the stored secret when the request carries the masked placeholder (or leaves the secret blank/omitted), but they took endpoint, host and username fields from the request. A user who can edit a record but cannot read its secret could point it at a host they control and capture the secret.

New rule, enforced by a shared helper assertStoredSecretTargetUnchanged (packages/server/src/utils/security/redaction.ts): when the request keeps the stored secret, every endpoint/host/username field must equal the stored value; otherwise the request fails with BAD_REQUEST "Re-enter the to change ".

  • Vault providers: mergeVaultProviderConfig (used by update and testConnection) binds kept credentials to url / siteUrl + clientId / region + endpoint + accessKeyId / vaultUri + tenantId + clientId / apiUrl. AWS and AWS Parameter Store accessKeyId is now masked as well.
  • Registries: updateRegistry no longer runs docker login with the stored password against a new registryUrl or username. organizationId is removed from apiUpdateRegistry.
  • Notifications: email (smtpServer, smtpPort, username with the stored password), Gotify (serverUrl with the stored app token), ntfy (serverUrl with the stored access token), custom (endpoint while stored header values are kept). updateEmail now passes the TRPCError message through instead of a generic one.
  • AI settings: a stored apiKey cannot be sent to a new apiUrl.
  • GitLab / Gitea: changing the URL, internal URL or client/application id requires entering the secret again. A URL change also clears the stored OAuth tokens, so the provider has to be authorized again. The GitLab edit form gets an optional "Application Secret" field for this (the Gitea form already asks for the client secret on every save).
  • DNS providers: Route53 accessKeyId and Porkbun apiKey are masked; the placeholder is restored on update and test as before.

What changes for users:

  • Owners/admins: editing names, flags, topics, recipients and other non-target fields works as before with the masked placeholder. Changing an address, username or region of an existing record now needs the secret again (clear error message). GitLab/Gitea need re-authorization after a URL change.
  • Members with update rights: can no longer redirect stored credentials to their own host.

The S3 destination router is not touched here.

Checks

  • CI=true bunx vitest run --config __test__/vitest.config.ts on the 7 new/changed test files: 45 passed.
  • Related folders (env, dns, registry, notification, ai, security, settings, permissions, Git providers): 68 files, 785 passed.
  • All 28 new/changed tests fail against the origin/AgentHits-Dev sources (checked by temporarily restoring the source files with git checkout origin/AgentHits-Dev -- <files>).
  • Full suite: 237 files passed; only application.real.test.ts fails locally (no Docker socket), as expected.
  • bun run typecheck: exit 0.
  • bunx biome check on the 18 changed files: 0 errors.

Rollout notes

  • No migrations.
  • After deploy, editing a vault provider, registry, notification, AI setting or GitLab/Gitea provider with a new address while keeping the stored secret returns BAD_REQUEST; re-enter the secret in the form.
  • AWS/AWS Parameter Store vault providers and Route53/Porkbun DNS providers now show the key id as ********; saving the form unchanged keeps the stored value.

🤖 Generated with Claude Code

Что:
- Общий хелпер assertStoredSecretTargetUnchanged: если запрос оставляет сохраненный секрет (плейсхолдер, пустое или отсутствующее значение), все поля адреса/хоста/логина должны совпадать с сохраненными, иначе BAD_REQUEST "Re-enter the <secret> to change <fields>".
- Vault-провайдеры: mergeVaultProviderConfig (update и testConnection) привязывает сохраненные креды к url/siteUrl/apiUrl/vaultUri/endpoint/region/tenantId/clientId/accessKeyId; accessKeyId для aws и aws-parameter-store теперь маскируется.
- Registry: updateRegistry не делает docker login с сохраненным паролем на новый registryUrl/username; organizationId убран из apiUpdateRegistry.
- Уведомления: email (smtpServer/smtpPort/username), gotify и ntfy (serverUrl), custom (endpoint при сохраненных заголовках); updateEmail теперь возвращает понятное сообщение TRPCError.
- AI: сохраненный apiKey нельзя отправить на новый apiUrl.
- GitLab/Gitea: смена URL или client id требует заново ввести secret; при смене URL OAuth-токены сбрасываются, провайдер нужно авторизовать заново. В форму редактирования GitLab добавлено необязательное поле Application Secret.
- DNS: accessKeyId (route53) и apiKey (porkbun) маскируются, плейсхолдер восстанавливается при update/test.
Зачем:
- Пользователь с правом редактирования, но без доступа к секрету, мог направить сохраненный токен или пароль на свой хост и перехватить его.
Риски:
- Смена адреса, логина или региона существующей записи теперь требует повторного ввода секрета; для GitLab/Gitea после смены URL нужна повторная авторизация.
- Записи со старыми ненормализованными адресами сравниваются и по сырому значению, но редкие расхождения формата потребуют повторного ввода секрета.
Проверки:
- Команды и результаты: `CI=true bunx vitest run --config __test__/vitest.config.ts` по 7 новым/измененным файлам: 45 passed; папки env, dns, registry, notification, ai, security, settings, permissions и провайдеров Git: 68 файлов, 785 passed; все новые тесты (28) падают на коде origin/AgentHits-Dev (исходники временно возвращены через checkout); полный прогон: 237 файлов passed, падает только application.real.test.ts (нет Docker); `bun run typecheck`: exit 0; `bunx biome check` по 18 измененным файлам: 0 ошибок.
- Ограничения: UI-формы и реальные внешние провайдеры (vault, registry, SMTP, GitLab/Gitea) вручную не проверялись.

What:
- Shared helper assertStoredSecretTargetUnchanged: when a request keeps a stored secret (placeholder, blank or omitted value), every endpoint/host/username field must equal the stored value, otherwise BAD_REQUEST "Re-enter the <secret> to change <fields>".
- Vault providers: mergeVaultProviderConfig (update and testConnection) binds stored credentials to url/siteUrl/apiUrl/vaultUri/endpoint/region/tenantId/clientId/accessKeyId; accessKeyId is now masked for aws and aws-parameter-store.
- Registry: updateRegistry no longer runs docker login with the stored password against a new registryUrl/username; organizationId is removed from apiUpdateRegistry.
- Notifications: email (smtpServer/smtpPort/username), gotify and ntfy (serverUrl), custom (endpoint while headers are kept); updateEmail now returns the TRPCError message.
- AI: a stored apiKey cannot be sent to a new apiUrl.
- GitLab/Gitea: changing the URL or client id requires the secret again; a URL change drops OAuth tokens so the provider must be authorized again. The GitLab edit form gets an optional Application Secret field.
- DNS: route53 accessKeyId and porkbun apiKey are masked; the placeholder is restored on update/test.
Why:
- A user who can edit a record but cannot read its secret could point the stored token or password at a host they control and capture it.
Risks:
- Changing the address, username or region of an existing record now requires entering the secret again; GitLab/Gitea need re-authorization after a URL change.
- Rows with older non-normalized addresses also match on the raw value, but rare format differences will ask for the secret again.
Checks:
- Commands and results: `CI=true bunx vitest run --config __test__/vitest.config.ts` on the 7 new/changed files: 45 passed; env, dns, registry, notification, ai, security, settings, permissions and Git provider folders: 68 files, 785 passed; all 28 new tests fail on origin/AgentHits-Dev code (sources temporarily restored with checkout); full run: 237 files passed, only application.real.test.ts fails (no Docker); `bun run typecheck`: exit 0; `bunx biome check` on the 18 changed files: 0 errors.
- Limitations: UI forms and real external providers (vault, registry, SMTP, GitLab/Gitea) were not checked manually.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@agentHits
agentHits merged commit 952c039 into AgentHits-Dev Oct 7, 2026
8 checks passed
@agentHits
agentHits deleted the fix/stored-secret-endpoint-binding branch October 7, 2026 05:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant