forked from envoyproxy/envoy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathossf_scorecard.py
executable file
·178 lines (155 loc) · 5.92 KB
/
ossf_scorecard.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/usr/bin/env python3
# Run OSSF Scorecard (https://github.com/ossf/scorecard) against Envoy dependencies.
#
# Usage:
#
# export API_PATH=api/
# tools/dependency/ossf_scorecard.py <path to repository_locations.bzl> \
# <path to scorecard binary> \
# <output CSV path>
#
# You will need to checkout and build the OSSF scorecard binary independently and supply it as a CLI
# argument.
#
# You will need to set a GitHub access token in the GITHUB_AUTH_TOKEN environment variable. You can
# generate personal access tokens under developer settings on GitHub. You should restrict the scope
# of the token to "repo: public_repo".
#
# The output is CSV suitable for import into Google Sheets.
from collections import namedtuple
import csv
import json
import os
import subprocess as sp
import sys
from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader
Scorecard = namedtuple(
'Scorecard', [
'name',
'contributors',
'active',
'ci_tests',
'pull_requests',
'code_review',
'fuzzing',
'security_policy',
'releases',
])
# Obtain GitHub project URL from a list of URLs.
def get_github_project_url(urls):
for url in urls:
if not url.startswith('https://github.com/'):
continue
components = url.split('/')
return f'https://github.com/{components[3]}/{components[4]}'
return None
# Shared Starlark/Python files must have a .bzl suffix for Starlark import, so
# we are forced to do this workaround.
def load_module(name, path):
spec = spec_from_loader(name, SourceFileLoader(name, path))
module = module_from_spec(spec)
spec.loader.exec_module(module)
return module
# this is the relative path in a bazel build
# to call this module outside of a bazel build set the `API_PATH` first,
# for example, if running from the envoy repo root: `export API_PATH=api/`
api_path = os.getenv("API_PATH", "external/envoy_api")
# Modules
repository_locations_utils = load_module(
'repository_locations_utils', os.path.join(api_path, 'bazel/repository_locations_utils.bzl'))
# Thrown on errors related to release date.
class OssfScorecardError(Exception):
pass
# We skip build, test, etc.
def is_scored_use_category(use_category):
return len(
set(use_category).intersection([
'dataplane_core', 'dataplane_ext', 'controlplane', 'observability_core',
'observability_ext'
])) > 0
def score(scorecard_path, repository_locations):
results = {}
for dep, metadata in sorted(repository_locations.items()):
if not is_scored_use_category(metadata['use_category']):
continue
results_key = metadata['project_name']
formatted_name = '=HYPERLINK("%s", "%s")' % (metadata['project_url'], results_key)
github_project_url = get_github_project_url(metadata['urls'])
if not github_project_url:
na = 'Not Scorecard compatible'
results[results_key] = Scorecard(
name=formatted_name,
contributors=na,
active=na,
ci_tests=na,
pull_requests=na,
code_review=na,
fuzzing=na,
security_policy=na,
releases=na)
continue
raw_scorecard = json.loads(
sp.check_output(
[scorecard_path, f'--repo={github_project_url}', '--show-details',
'--format=json']))
checks = {c['CheckName']: c for c in raw_scorecard['Checks']}
# Generic check format.
def _format(key):
score = checks[key]
status = score['Pass']
confidence = score['Confidence']
return f'{status} ({confidence})'
# Releases need to be extracted from Signed-Releases.
def release_format():
score = checks['Signed-Releases']
if score['Pass']:
return _format('Signed-Releases')
details = score['Details']
release_found = details is not None and any('release found:' in d for d in details)
if release_found:
return 'True (10)'
else:
return 'False (10)'
results[results_key] = Scorecard(
name=formatted_name,
contributors=_format('Contributors'),
active=_format('Active'),
ci_tests=_format('CI-Tests'),
pull_requests=_format('Pull-Requests'),
code_review=_format('Code-Review'),
fuzzing=_format('Fuzzing'),
security_policy=_format('Security-Policy'),
releases=release_format())
print(raw_scorecard)
print(results[results_key])
return results
def print_csv_results(csv_output_path, results):
headers = Scorecard._fields
with open(csv_output_path, 'w') as f:
writer = csv.writer(f)
writer.writerow(headers)
for name in sorted(results):
writer.writerow(getattr(results[name], h) for h in headers)
if __name__ == '__main__':
if len(sys.argv) != 4:
print(
'Usage: %s <path to repository_locations.bzl> <path to scorecard binary> <output CSV path>'
% sys.argv[0])
sys.exit(1)
access_token = os.getenv('GITHUB_AUTH_TOKEN')
if not access_token:
print('Missing GITHUB_AUTH_TOKEN')
sys.exit(1)
path = sys.argv[1]
scorecard_path = sys.argv[2]
csv_output_path = sys.argv[3]
spec_loader = repository_locations_utils.load_repository_locations_spec
path_module = load_module('repository_locations', path)
try:
results = score(scorecard_path, spec_loader(path_module.REPOSITORY_LOCATIONS_SPEC))
print_csv_results(csv_output_path, results)
except OssfScorecardError as e:
print(
f'An error occurred while processing {path}, please verify the correctness of the '
f'metadata: {e}')