Merge request discussion JSON endpoint exposes all User attributes/columns including secrets, some encrypted, some not
Steps to reproduce:
- Create a merge request
- Start a discussion on the diff
- Resolve the discussion
- Request
/:namespace/:project/merge_requests/:iid/discussions.json
- Each JSON array element represents a discussion. Each discussion has a
resolved_by
attribute that contains the full JSON representation of theUser
record of the person who resolved the discussion, which in this case would be you.
In my case it looks like this:
"resolved_by": {
"id": 87854,
"email": "douwe@gitlab.com",
"created_at": "2015-01-20T14:14:06.510Z",
"updated_at": "2018-09-27T08:21:41.743Z",
"name": "Douwe Maan",
"admin": false,
"projects_limit": 100000,
"skype": "",
"linkedin": "",
"twitter": "DouweM",
"bio": "Engineering Manager, Create at GitLab",
"username": "DouweM",
"can_create_group": true,
"can_create_team": false,
"state": "active",
"color_scheme_id": 2,
"password_expires_at": null,
"created_by_id": null,
"avatar": {
"url": "https://storage.googleapis.com/gitlab-gprd-uploads/user/avatar/87854/avatar.png"
},
"hide_no_ssh_key": false,
"website_url": "https://douwe.me",
"last_credential_check_at": null,
"admin_email_unsubscribed_at": null,
"notification_email": "douwe@gitlab.com",
"hide_no_password": false,
"password_automatically_set": false,
"location": "Utrecht, the Netherlands",
"public_email": "",
"encrypted_otp_secret": "<snip, encrypted using `Gitlab::Application.secrets.otp_key_base` and the next two attributes>",
"encrypted_otp_secret_iv": "<snip>",
"encrypted_otp_secret_salt": "<snip>",
"otp_required_for_login": true,
"otp_backup_codes": [
"<snip, hashed using BCrypt>",
"<snip>",
"<snip>",
"<snip>",
"<snip>",
"<snip>",
"<snip>",
"<snip>",
"<snip>",
"<snip>"
],
"dashboard": "projects",
"project_view": "files",
"consumed_timestep": 51259923,
"layout": "fluid",
"hide_project_limit": false,
"note": "",
"otp_grace_period_started_at": "2017-12-05T10:58:54.326Z",
"external": false,
"organization": "GitLab",
"incoming_email_token": "<snip, unencrypted>",
"auditor": false,
"ghost": null,
"require_two_factor_authentication_from_group": true,
"two_factor_grace_period": 48,
"notified_of_own_activity": null,
"support_bot": null,
"last_activity_on": "2018-09-27",
"preferred_language": "en",
"email_opted_in": null,
"email_opted_in_ip": null,
"email_opted_in_source_id": null,
"email_opted_in_at": null,
"theme_id": 1,
"accepted_term_id": 3,
"feed_token": "<snip, unencrypted>",
"private_profile": false,
"roadmap_layout": "months",
"include_private_contributions": null
}
All of the <snip>
s (encrypted_otp_secret
, encrypted_otp_secret_iv
, encrypted_otp_secret_salt
, otp_backup_codes
, incoming_email_token
, feed_token
) are obviously sensitive. The first 4 are encrypted, but the incoming_email_token
and feed_token
are not, and the one can be used to create issues/comments as the user in question, and the other to read RSS feeds that can contain all kinds of sensitive information.
The underlying issue is that expose :resolved_by
inside the DiscussionEntity
does not specify using: NoteUserEntity
like it does on NoteEntity
, which would have limited the exposed attributes to a safe set.
We include BlocksJsonSerialization
on User
to prevent exactly this scenario; I don't know why it didn't work in this case. cc @rspeicher
The issue was introduced in https://gitlab.com/gitlab-org/gitlab-ce/commit/3e66795ef1ff1228906239763910b051d8afcc37, which went into 11.1.0.