HexBrain

Discourse · Report #3689633

Hidden/restricted tags can be mutated through synonym ID paths without per-tag authorization

Validstatus
mediumseverity
31upvotes
26 Aug 2026disclosed

How it was found

Discourse requires both tag-edit permission and tag visibility for a user to edit a tag. However, the ID-based synonym mutation paths do not consistently enforce that rule for each tag being modified.

TagsController#create_synonyms authorizes only the target tag (guardian.ensure_can_edit_tag!(@tag)) and then forwards attacker-supplied synonym tag IDs to DiscourseTagging.add_or_create_synonyms without verifying that the user can see or edit each referenced synonym tag.

TagSettingsUpdater has the same issue for new_synonyms and removed_synonym_ids: it accepts tag IDs and mutates synonym relationships without per-synonym authorization checks.

In local reproduction on the 2026.4.0-latest source:

  1. a non-admin tag editor could not view a hidden tag (GET /tag/:hidden_id/info.json returned 404),
  2. but the same user could still submit the hidden tag's numeric ID to /tag/:visible_id/synonyms.json and /tag/:visible_id/settings.json,
  3. and both paths updated the hidden tag's target_tag_id successfully.

This is an authorization bypass affecting hidden/restricted tag integrity. If the vendor considers a similar hidden-tag synonym issue already fixed, this should be triaged as an incomplete fix / variant.

  1. Create a non-admin user in edit_tags_allowed_groups.
  2. Create one visible tag the user can edit.
  3. Create one hidden/restricted tag in a tag group the user cannot access.
  4. Sign in as the non-admin tag editor.
  5. Request /tag/<hidden_tag_id>/info.json.
  6. Observe that the response is 404, confirming the hidden tag is not visible.
  7. Send POST /tag/<visible_tag_id>/synonyms.json with tags[0][id]=<hidden_tag_id>.
  8. Observe that the request succeeds and the hidden tag becomes a synonym of the visible tag.
  9. Reset state and send PUT /tag/<visible_tag_id>/settings.json with tag_settings[new_synonyms][0][id]=<hidden_tag_id>.
  10. Observe that this also succeeds and updates the hidden tag's synonym relationship.

2 examples, 0 failures

Impact

Summary:

A non-admin user with tag-editing permission can modify hidden or restricted tags by supplying their numeric IDs to the synonym creation and tag settings endpoints. Although the user cannot view the hidden tag, the controller only authorizes the visible target tag and does not re-check authorization for each synonym tag ID, allowing hidden tags to be reassigned as synonyms of a visible tag.

This allows a tag editor to modify the synonym relationship of a hidden or restricted tag they are not allowed to view or edit. The practical impact is unauthorized mutation of hidden tag taxonomy and integrity corruption of tag relationships through numeric-ID paths that skip per-synonym authorization checks.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original