Skip to content

v5 password-and-description resource-type update via --password fails with 400 duplicate secret #135

Description

@NextGenITSolutionsGmbH

Environment

  • go-passbolt-cli: v0.5.0 (via passbolt --version returns "go-passbolt-cli")
  • Passbolt server: CE 5.11.0 (self-hosted)
  • Resource type: password-and-description (v4, type UUID a28a04cd-6f53-518a-967c-9963bf9cec51)
  • Operator context: single Passbolt user, sole ACL owner of the resource

Bug reproduction

Running passbolt update resource --id <UUID> --password <NEW_VALUE> on a resource of type password-and-description deterministically returns HTTP 400:

Raw Response: {
  "header": {
    "status": "error",
    "message": "Could not validate resource data.",
    "code": 400
  },
  "body": {
    "secrets": {
      "1": {
        "user_id": {
          "secret_unique": "A secret already exists for the given user and resource.",
          "secret_revision_unique": "A secret already exists for the given user, resource and secret revision."
        }
      }
    }
  }
}

Reproduced 3× on 2026-07-07 with different resource IDs and different password values.

Root cause (verified against upstream passbolt_api source)

The update resource --password code path builds the secrets[] PUT payload with TWO entries for the same user_id:

  • entry 0: encrypted password blob
  • entry 1: encrypted description blob (re-encrypted from existing description)

The v5 SecretsTable.buildRules() in passbolt_api/src/Model/Table/SecretsTable.php registers isUnique(['user_id', 'resource_id', 'deleted']) under addCreate with allowMultipleNulls=false. Two entries with the same user_id violate this constraint.

The correct wire format for password-and-description (v4) is ONE secrets[] entry per user with a merged JSON {"password":"...","description":"..."} encrypted as a single PGP blob.

References:

  • passbolt_api/src/Model/Table/SecretsTable.phpisUnique constraint
  • passbolt_api/src/Model/Table/ResourcesTable.phpisSecretsProvidedRule requires ONE secret per accessing user

Observed workaround (also verified)

Using passbolt update resource --id <UUID> --secret-field password=<NEW_VALUE> instead of --password <NEW_VALUE> succeeds. The --secret-field code path apparently performs a fetch-merge-encrypt round-trip: fetches the existing secret, decrypts it, merges the new field, re-encrypts as one blob, and PUTs one secrets[] entry. This preserves resource ID + audit history.

Suggested fix

Align the update resource --password code path with --secret-field password=: perform the fetch-merge-encrypt round-trip when the resource type is password-and-description (v4) or v5-default (v5-encrypted-metadata), rather than sending separate secret entries for each field.

Impact

Any automation that rotates a password value on password-and-description resources via the CLI hits a hard failure. Current NGIS workaround is delete+recreate (loses resource ID and audit history — invasive) or migration to --secret-field (preferred but not currently documented as the canonical update pattern).

References

  • NGIS internal Passbolt Reference Dossier (2026-07-07): section "Executive Summary §1 + §3" documents this bug with the root-cause diagnosis and workaround
  • Community forum #7637 (previously reported symptom under different resource type)

Willingness to help

Happy to submit a PR against cmd/updateResource.go that inserts the fetch-merge-encrypt round-trip when the resource type schema has multiple secret fields. Also happy to add a regression test that reproduces the current bug and passes with the fix.

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