Skip to content

Commit

Permalink
Add GH org permission check (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
mic67mel authored May 17, 2021
1 parent f5e3ef6 commit ee72cf0
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# [0.15.0](https://github.com/ComplianceAsCode/auditree-arboretum/releases/tag/v0.15.0)

- [ADDED] Github org permissions check added to `permissions`.

# [0.14.0](https://github.com/ComplianceAsCode/auditree-arboretum/releases/tag/v0.14.0)

- [ADDED] Auditree evidence locker large file check has been added.
Expand Down
2 changes: 1 addition & 1 deletion arboretum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
# limitations under the License.
"""Arboretum - Checking your compliance & security posture, continuously."""

__version__ = '0.14.0'
__version__ = '0.15.0'
49 changes: 48 additions & 1 deletion arboretum/permissions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,52 @@ how to include the fetchers and checks from this library in your downstream proj

## Checks

### Organization Integrity (Repository Permissions)

* Class: [OrgPermissionsCheck][org-permissions-check]
* Purpose: Ensure that `direct` collaborators and forks do not exist in the organization repositories.
* Behavior: Collaborators and forks are checked for every repository.
A failure is generated when `direct` collaborators are found in a repository. A warning is generated for each fork found for a repository.
* Evidence depended upon:
* `direct` collaborators found in organization repositories.
* `raw/permissions/<gh|gl|bb>_direct_collaborators_<org_url_hash>.json`
* `outside` collaborators found in organization repositories.
* `raw/permissions/<gh|gl|bb>_outside_collaborators_<org_url_hash>.json`
* forks found in organization repositories.
* `raw/permissions/<gh|gl|bb>_forks_<org_url_hash>.json`
* NOTE: Only gh (Github) is currently supported by this check. Gitlab and Bitbucket support coming soon...
* Configuration elements:
* `org.permissions.org_integrity.orgs`
* Required
* List of dictionaries:
* `url`
* Required
* Organization URL (string).

* Example configuration:

```json
{
"org": {
"permissions": {
"org_integrity": {
"orgs": [
{
"url": "https://github.com/my-org-1"
}
]
}
}
}
}
```

* Import statement:

```python
from arboretum.permissions.checks.test_org_permissions import OrgPermissionsCheck
```

### Organization Integrity (Repository Collaborators)

* Class: [OrgCollaboratorsCheck][org-collaborators-check]
Expand Down Expand Up @@ -163,4 +209,5 @@ direct collaborators matching the exceptions are found.
[usage]: https://github.com/ComplianceAsCode/auditree-arboretum#usage
[fetch-org-permissions]: https://github.com/ComplianceAsCode/auditree-arboretum/blob/main/arboretum/permissions/fetchers/github/fetch_org_permissions.py
[repository-permissions]: https://docs.github.com/en/free-pro-team@latest/github/setting-up-and-managing-organizations-and-teams/repository-permission-levels-for-an-organization
[org-collaborators-check]: https://github.com/ComplianceAsCode/auditree-arboretum/blob/main/arboretum/permissions/checks/test_org_collaborators.py
[org-collaborators-check]: https://github.com/ComplianceAsCode/auditree-arboretum/blob/main/arboretum/permissions/checks/test_org_collaborators.py
[org-permissions-check]: https://github.com/ComplianceAsCode/auditree-arboretum/blob/main/arboretum/permissions/checks/test_org_permissions.py
109 changes: 109 additions & 0 deletions arboretum/permissions/checks/test_org_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# -*- mode:python; coding:utf-8 -*-
# Copyright (c) 2021 IBM Corp. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Repository organization permissions check."""

from compliance.check import ComplianceCheck
from compliance.evidence import DAY, ReportEvidence, evidences
from compliance.utils.data_parse import get_sha256_hash


class OrgPermissionsCheck(ComplianceCheck):
"""Checks for repository organization/owner permissions."""

@property
def title(self):
"""
Return the title of the checks.
:returns: the title of the checks
"""
return 'Repository Organization/Owner Permissions'

@classmethod
def setUpClass(cls):
"""Initialize the check object with configuration settings."""
cls.config.add_evidences(
[
ReportEvidence(
'org_permissions.md',
'permissions',
DAY,
'Repository organization permission report.'
)
]
)
return cls

def test_org_permissions(self):
"""Check the access to organization repositories."""
orgs = self.config.get('org.permissions.org_integrity.orgs')
for org in orgs:
host, org_name = org['url'].rsplit('/', 1)
service = 'gh'
if 'gitlab' in host:
service = 'gl'
elif 'bitbucket' in host:
service = 'bb'
url_hash = get_sha256_hash([org['url']], 10)
path = 'raw/permissions/{}_{}_{}.json'
evs = ['direct_collaborators', 'outside_collaborators', 'forks']
ev_paths = {ev: path.format(service, ev, url_hash) for ev in evs}
with evidences(self, ev_paths) as evidence:
self._generate_results(org_name, evidence)

def get_notification_message(self):
"""
Repository Organization/Owner Permissions check notifier.
:returns: notification dictionary
"""
return {'body': None}

def get_reports(self):
"""
Provide the check report name.
:returns: the report(s) generated for this check
"""
return ['permissions/org_permissions.md']

def _generate_results(self, org, ev):
self.add_failures(
org,
self._check_collabs(
ev['direct_collaborators'].content_as_json,
ev['outside_collaborators'].content_as_json
)
)
self.add_warnings(org, self._check_forks(ev['forks'].content_as_json))

def _check_collabs(self, ev_direct, ev_outside):
repo_collabs = []
for repo, collab_list in ev_direct.items():
collabs = []
for collab in collab_list:
collab['member'] = collab not in ev_outside[repo]
collabs.append(collab)
if collabs:
repo_collabs.append({'repo': repo, 'collabs': collabs})
return repo_collabs

def _check_forks(self, evidence):
repo_forks = []
for repo, fork_list in evidence.items():
forks = [fork['html_url'] for fork in fork_list]
if forks:
repo_forks.append({'repo': repo, 'forks': forks})
return repo_forks
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{#- -*- mode:jinja2; coding: utf-8 -*- -#}
{#
Copyright (c) 2021 IBM Corp. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
#}
# {{ test.title }} {{ now.strftime('%Y-%m-%d') }}

{% if all_failures|length > 0 %}
<b>Repositories with single collaborators:</b>
{% endif %}
{% for org, repocollabsdict in all_failures.items() %}
{% for repocollabs in repocollabsdict %}
<ul>
<details>
<summary>{{org+'/'+repocollabs.repo}}</summary>
{% for collaborator in repocollabs.collabs%}
<ul>
<details>
<summary>{{collaborator.login}} {{ ' - Organization Member' if collaborator.member }}</summary>
<ul>
{% for permission, permissionvalue in collaborator.permissions.items() %}
{% if permissionvalue%}
<li>{{ permission }}</li>
{% endif %}
{% endfor %}
</ul>
</details>
</ul>
{% endfor %}
</details>
</ul>
{% endfor %}
{% endfor %}

{% if all_warnings|length > 0 %}
<b>Repositories with forks:</b>
{% endif %}
{% for org, repoforksdict in all_warnings.items() %}
{% for repoforks in repoforksdict %}
<ul>
<details>
<summary>{{org+'/'+repoforks.repo}}</summary>
{% for fork in repoforks.forks%}
<ul>
<li>{{ fork }}</li>
</ul>
{% endfor %}
</details>
</ul>
{% endfor %}
{% endfor %}

0 comments on commit ee72cf0

Please sign in to comment.