Skip to content

Azure DevOps sync ignores the Disable Secret Deletion option #7609

Description

@omlahore

Product

Secret Sync

Describe the bug

disableSecretDeletion is enforced individually by each secret sync rather than centrally in secret-sync-fns.ts. Of the 44 destinations with a *-sync-fns.ts, 43 read the flag somewhere in their sync path. azure-devops is the only one whose sync functions never reference it at all:

$ grep -rn "disableSecretDeletion" backend/src/services/secret-sync/azure-devops/
$ 

The comparable destinations all guard their prune step, in one of a few shapes:

// vercel, 1password, databricks, ... 
if (secretSync.syncOptions.disableSecretDeletion) return;

// aws-parameter-store, aws-secrets-manager
if (syncOptions.disableSecretDeletion) return { createdSecretKeys, updatedSecretKeys, deletedSecretKeys };

// gcp
if (secretSync.syncOptions.disableSecretDeletion) continue;

Why it matters here specifically

azure-devops-sync-fns.ts builds its payload from the Infisical secret map alone, then PUTs the whole variable group:

const variables: Record<string, { value: string; isSecret: boolean }> = {};
for (const [key, secret] of Object.entries(secretMap)) {
  if (secret?.value !== undefined) {
    variables[key] = { value: secret.value, isSecret: true };
  }
}
...
await request.put(url, {
  name: groupName,
  description: groupName,
  type: "Vsts",
  variables,
  variableGroupProjectReferences: [...]
}, { ... });

Nothing reads the existing group's variables first, so whatever the destination already had is not carried into the request body. The Update variable group reference describes the body field as "Sets variables contained in the variable group", which reads as replacement rather than merge on a PUT.

I could not verify the delete-on-omit behaviour against a real Azure DevOps organisation, so I am flagging rather than asserting it. Either reading is still a bug:

  • If the PUT replaces, a user who enables "Disable Secret Deletion" still loses every variable in the group that is not managed by Infisical, which is the exact scenario the option exists to prevent.
  • If the PUT merges, then this destination has no prune step at all, and the option is offered in the UI while doing nothing here.

To Reproduce

  1. Create an Azure DevOps secret sync and enable Disable Secret Deletion in the sync options.
  2. Add a variable directly in the destination variable group that does not exist in the Infisical environment.
  3. Trigger a sync.
  4. Check whether the manually added variable is still present.

Expected behavior

Same as every other destination: when disableSecretDeletion is set, a sync should not remove destination secrets that are absent from Infisical.

Additional context

Checked azure-entra-id-scim too, since it also has no reference to the flag in its sync functions, but it does not appear to have a prune step, so I do not think it is affected.

The wider observation is that the flag is reimplemented 43 separate times in at least seven different shapes. Enforcing it once around the prune step would make this class of gap impossible rather than relying on each new destination remembering. Happy to open a PR for the azure-devops case on its own, or for the centralisation if that is wanted, but the latter touches every destination so it seemed worth asking first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions