CVE-2018-7206: Insufficient Information (NVD-CWE-noinfo)
value: An issue was discovered in Project Jupyter JupyterHub OAuthenticator 0.6.x before 0.6.2 and 0.7.x before 0.7.3. When using JupyterHub with GitLab group whitelisting for access control, group membership was not checked correctly, allowing members not in the whitelisted groups to create accounts on the Hub. (Users were not allowed to access other users' accounts, but could create their own accounts on the Hub linked to their GitLab account. GitLab authentication not using gitlab_group_whitelist is unaffected. No other Authenticators are affected.)
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 8.8
Published: 2018-02-18T03:29Z
Repository: https://github.com/jupyterhub/oauthenticator
Language: Python
Files changed: oauthenticator/gitlab.py
Commit date: 2018-02-16 17:18:01 +0100
Commit message: Security fix: fix gitlab group whitelist check

check group membership of users

previously, group_whitelist allowed any user who could access the groups in the whitelist,
rather than checking their membership of the groups.

Admin users received the correct behavior already

Diff: diff --git a/oauthenticator/gitlab.py b/oauthenticator/gitlab.py
index e5d9029..e6669c2 100644
--- a/oauthenticator/gitlab.py
+++ b/oauthenticator/gitlab.py
@@ -19,11 +19,10 @@ from tornado.httpclient import HTTPRequest, AsyncHTTPClient
 
 from jupyterhub.auth import LocalAuthenticator
 
 from traitlets import Set
 
-from .common import next_page_from_links
 from .oauth2 import OAuthLoginHandler, OAuthenticator
 
 # Support gitlab.com and gitlab community edition installations
 GITLAB_HOST = os.environ.get('GITLAB_HOST') or 'https://gitlab.com'
 GITLAB_API_VERSION = os.environ.get('GITLAB_API_VERSION') or '4'
@@ -129,35 +128,18 @@ class GitLabOAuthenticator(OAuthenticator):
 
     @gen.coroutine
     def _check_group_whitelist(self, username, user_id, is_admin, access_token):
         http_client = AsyncHTTPClient()
         headers = _api_headers(access_token)
-        if is_admin:
-            # For admins, /groups returns *all* groups. As a workaround
-            # we check if we are a member of each group in the whitelist
-            for group in map(url_escape, self.gitlab_group_whitelist):
-                url = "%s/groups/%s/members/%d" % (GITLAB_API, group, user_id)
-                req = HTTPRequest(url, method="GET", headers=headers)
-                resp = yield http_client.fetch(req, raise_error=False)
-                if resp.code == 200:
-                    return True  # user _is_ in group
-        else:
-            # For regular users we get all the groups to which they have access
-            # and check if any of these are in the whitelisted groups
-            next_page = url_concat("%s/groups" % GITLAB_API,
-                                   dict(all_available=True))
-            while next_page:
-                req = HTTPRequest(next_page, method="GET", headers=headers)
-                resp = yield http_client.fetch(req)
-                resp_json = json.loads(resp.body.decode('utf8', 'replace'))
-                next_page = next_page_from_links(resp)
-                user_groups = set(entry["path"] for entry in resp_json)
-                # check if any of the organizations seen thus far are in whitelist
-                if len(self.gitlab_group_whitelist & user_groups) > 0:
-                    return True
-            return False
-
+        # Check if we are a member of each group in the whitelist
+        for group in map(url_escape, self.gitlab_group_whitelist):
+            url = "%s/groups/%s/members/%d" % (GITLAB_API, group, user_id)
+            req = HTTPRequest(url, method="GET", headers=headers)
+            resp = yield http_client.fetch(req, raise_error=False)
+            if resp.code == 200:
+                return True  # user _is_ in group
+        return False
 
 
 class LocalGitLabOAuthenticator(LocalAuthenticator, GitLabOAuthenticator):
 
     """A version that mixes in local system user creation"""

Vulnerable code: from .common import next_page_from_links
        if is_admin:
            # For admins, /groups returns *all* groups. As a workaround
            # we check if we are a member of each group in the whitelist
            for group in map(url_escape, self.gitlab_group_whitelist):
                url = "%s/groups/%s/members/%d" % (GITLAB_API, group, user_id)
                req = HTTPRequest(url, method="GET", headers=headers)
                resp = yield http_client.fetch(req, raise_error=False)
                if resp.code == 200:
                    return True  # user _is_ in group
        else:
            # For regular users we get all the groups to which they have access
            # and check if any of these are in the whitelisted groups
            next_page = url_concat("%s/groups" % GITLAB_API,
                                   dict(all_available=True))
            while next_page:
                req = HTTPRequest(next_page, method="GET", headers=headers)
                resp = yield http_client.fetch(req)
                resp_json = json.loads(resp.body.decode('utf8', 'replace'))
                next_page = next_page_from_links(resp)
                user_groups = set(entry["path"] for entry in resp_json)
                # check if any of the organizations seen thus far are in whitelist
                if len(self.gitlab_group_whitelist & user_groups) > 0:
                    return True
            return False

Fixed code:         # Check if we are a member of each group in the whitelist
        for group in map(url_escape, self.gitlab_group_whitelist):
            url = "%s/groups/%s/members/%d" % (GITLAB_API, group, user_id)
            req = HTTPRequest(url, method="GET", headers=headers)
            resp = yield http_client.fetch(req, raise_error=False)
            if resp.code == 200:
                return True  # user _is_ in group
        return False