Skip to content

Commit d38da22

Browse files
authored
Merge pull request #3 from advanced-security/copilot/refactor-tls-support-in-scripts
Add TLS certificate bundle support for self-signed certificates
2 parents 1dedea5 + fb427aa commit d38da22

9 files changed

+239
-27
lines changed

README.md

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ This script retrieves code scanning alerts from GitHub repositories, organizatio
7676

7777
```text
7878
usage: list_code_scanning_alerts.py [-h] [--scope {ent,org,repo}] [--state {open,resolved}] [--since SINCE] [--json]
79-
[--raw] [--quote-all] [--hostname HOSTNAME] [--debug]
79+
[--raw] [--quote-all] [--hostname HOSTNAME] [--ca-cert-bundle CA_CERT_BUNDLE]
80+
[--no-verify-tls] [--debug]
8081
name
8182
8283
List code scanning alerts for a GitHub repository, organization or Enterprise.
@@ -97,6 +98,9 @@ options:
9798
--raw, -r Output raw JSON data from the API
9899
--quote-all, -q Quote all fields in CSV output
99100
--hostname HOSTNAME GitHub Enterprise hostname (defaults to github.com)
101+
--ca-cert-bundle CA_CERT_BUNDLE, -C CA_CERT_BUNDLE
102+
Path to CA certificate bundle in PEM format (e.g. for self-signed server certificates)
103+
--no-verify-tls Do not verify TLS connection certificates (warning: insecure)
100104
--debug, -d Enable debug logging
101105
```
102106

@@ -106,7 +110,8 @@ This script replays or restores the status of code scanning alerts based on a pr
106110

107111
```text
108112
usage: replay_code_scanning_alert_status.py [-h] [--scope {ent,org,repo}] [--state {open,resolved}] [--since SINCE]
109-
[--json] [--quote-all] [--hostname HOSTNAME] [--debug]
113+
[--json] [--quote-all] [--hostname HOSTNAME]
114+
[--ca-cert-bundle CA_CERT_BUNDLE] [--no-verify-tls] [--debug]
110115
name
111116
112117
Replay code scanning alert status for a GitHub repository, organization or Enterprise, based on a provide file of
@@ -127,6 +132,9 @@ options:
127132
--json Output in JSON format (otherwise CSV)
128133
--quote-all, -q Quote all fields in CSV output
129134
--hostname HOSTNAME GitHub Enterprise hostname (defaults to github.com)
135+
--ca-cert-bundle CA_CERT_BUNDLE, -C CA_CERT_BUNDLE
136+
Path to CA certificate bundle in PEM format (e.g. for self-signed server certificates)
137+
--no-verify-tls Do not verify TLS connection certificates (warning: insecure)
130138
--debug, -d Enable debug logging
131139
```
132140

@@ -136,7 +144,8 @@ This script replays or restores the status of secret scanning alerts based on a
136144

137145
```text
138146
usage: replay_secret_scanning_result_status.py [-h] [--scope {ent,org,repo}] [--state {open,resolved}] [--since SINCE]
139-
[--json] [--quote-all] [--hostname HOSTNAME] [--debug]
147+
[--json] [--quote-all] [--hostname HOSTNAME]
148+
[--ca-cert-bundle CA_CERT_BUNDLE] [--no-verify-tls] [--debug]
140149
name
141150
142151
Replay secret scanning alert status for a GitHub repository, organization or Enterprise, based on a provided file of
@@ -159,6 +168,9 @@ options:
159168
--json Output in JSON format (otherwise CSV)
160169
--quote-all, -q Quote all fields in CSV output
161170
--hostname HOSTNAME GitHub Enterprise hostname (defaults to github.com)
171+
--ca-cert-bundle CA_CERT_BUNDLE, -C CA_CERT_BUNDLE
172+
Path to CA certificate bundle in PEM format (e.g. for self-signed server certificates)
173+
--no-verify-tls Do not verify TLS connection certificates (warning: insecure)
162174
--debug, -d Enable debug logging
163175
```
164176

@@ -207,7 +219,8 @@ This script identifies and resolves duplicate secret scanning alerts that occur
207219

208220
```text
209221
usage: resolve_duplicate_secret_scanning_alerts.py [-h] [--scope {ent,org,repo}] [--state {open,resolved}]
210-
[--since SINCE] [--hostname HOSTNAME] [--debug]
222+
[--since SINCE] [--hostname HOSTNAME]
223+
[--ca-cert-bundle CA_CERT_BUNDLE] [--no-verify-tls] [--debug]
211224
[--add-matching-secret OLD_TYPE NEW_TYPE]
212225
name
213226
@@ -226,6 +239,9 @@ options:
226239
Only show alerts created after this date/time - ISO 8601 format, e.g. 2024-10-08 or
227240
2024-10-08T12:00; or Nd format, e.g. 7d for 7 days ago
228241
--hostname HOSTNAME GitHub Enterprise hostname (defaults to github.com)
242+
--ca-cert-bundle CA_CERT_BUNDLE, -C CA_CERT_BUNDLE
243+
Path to CA certificate bundle in PEM format (e.g. for self-signed server certificates)
244+
--no-verify-tls Do not verify TLS connection certificates (warning: insecure)
229245
--debug, -d Enable debug logging
230246
--add-matching-secret OLD_TYPE NEW_TYPE, -a OLD_TYPE NEW_TYPE
231247
Add a new pair of matched secret types
@@ -236,7 +252,8 @@ options:
236252
This script bulk-closes all open code scanning alerts for a specified repository. It's useful for cleanup operations, such as dismissing false positives or marking alerts as "won't fix" across an entire repository. The script supports dry-run mode to preview changes before applying them.
237253

238254
```text
239-
usage: close_code_scanning_alerts.py [-h] [--resolution {false positive,won't fix,used in tests}] [--dry-run] [-d]
255+
usage: close_code_scanning_alerts.py [-h] [--resolution {false positive,won't fix,used in tests}] [--dry-run]
256+
[--hostname HOSTNAME] [--ca-cert-bundle CA_CERT_BUNDLE] [--no-verify-tls] [-d]
240257
repo_name
241258
242259
Close all open code scanning alerts for a repository.
@@ -249,6 +266,10 @@ options:
249266
--resolution {false positive,won't fix,used in tests}
250267
The resolution of the alert.
251268
--dry-run Print the alerts that would be closed, but don't actually close them.
269+
--hostname HOSTNAME GitHub Enterprise hostname (defaults to github.com)
270+
--ca-cert-bundle CA_CERT_BUNDLE, -C CA_CERT_BUNDLE
271+
Path to CA certificate bundle in PEM format (e.g. for self-signed server certificates)
272+
--no-verify-tls Do not verify TLS connection certificates (warning: insecure)
252273
-d, --debug Print debug messages to the console.
253274
```
254275

close_code_scanning_alerts.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,25 @@ def add_args(parser: argparse.ArgumentParser) -> None:
9191
action="store_true",
9292
help="Print the alerts that would be closed, but don't actually close them.",
9393
)
94+
parser.add_argument(
95+
"--hostname",
96+
type=str,
97+
default="github.com",
98+
required=False,
99+
help="GitHub Enterprise hostname (defaults to github.com)",
100+
)
101+
parser.add_argument(
102+
"--ca-cert-bundle",
103+
"-C",
104+
type=str,
105+
required=False,
106+
help="Path to CA certificate bundle in PEM format (e.g. for self-signed server certificates)"
107+
)
108+
parser.add_argument(
109+
"--no-verify-tls",
110+
action="store_true",
111+
help="Do not verify TLS connection certificates (warning: insecure)"
112+
)
94113
parser.add_argument(
95114
"-d",
96115
"--debug",
@@ -108,7 +127,18 @@ def main() -> None:
108127

109128
logging.basicConfig(level=logging.INFO if not args.debug else logging.DEBUG)
110129

111-
github = GitHub()
130+
verify = True
131+
if args.ca_cert_bundle:
132+
verify = args.ca_cert_bundle
133+
134+
if args.no_verify_tls:
135+
verify = False
136+
LOG = logging.getLogger(__name__)
137+
LOG.warning("Disabling TLS verification. This is insecure and should not be used in production")
138+
import urllib3
139+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
140+
141+
github = GitHub(hostname=args.hostname, verify=verify)
112142

113143
try:
114144
owner, repo = args.repo_name.split("/")

list_code_scanning_alerts.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ def output_csv(results: list[dict], quote_all: bool) -> None:
124124
writer.writerow(to_list(result))
125125

126126

127-
def list_code_scanning_alerts(name: str, scope: str, hostname: str, state: str|None=None, since: datetime.datetime|None=None, raw: bool=False) -> Generator[dict, None, None]:
128-
g = GitHub(hostname=hostname)
127+
def list_code_scanning_alerts(name: str, scope: str, hostname: str, state: str|None=None, since: datetime.datetime|None=None, raw: bool=False, verify: bool | str = True) -> Generator[dict, None, None]:
128+
g = GitHub(hostname=hostname, verify=verify)
129129
alerts = g.list_code_scanning_alerts(name, state=state, since=since, scope=scope)
130130
if raw:
131131
return alerts
@@ -178,6 +178,18 @@ def add_args(parser: argparse.ArgumentParser) -> None:
178178
required=False,
179179
help="GitHub Enterprise hostname (defaults to github.com)",
180180
)
181+
parser.add_argument(
182+
"--ca-cert-bundle",
183+
"-C",
184+
type=str,
185+
required=False,
186+
help="Path to CA certificate bundle in PEM format (e.g. for self-signed server certificates)"
187+
)
188+
parser.add_argument(
189+
"--no-verify-tls",
190+
action="store_true",
191+
help="Do not verify TLS connection certificates (warning: insecure)"
192+
)
181193
parser.add_argument(
182194
"--debug", "-d", action="store_true", help="Enable debug logging"
183195
)
@@ -202,11 +214,21 @@ def main() -> None:
202214
name = args.name
203215
state = args.state
204216
hostname = args.hostname
217+
verify = True
218+
219+
if args.ca_cert_bundle:
220+
verify = args.ca_cert_bundle
221+
222+
if args.no_verify_tls:
223+
verify = False
224+
LOG.warning("Disabling TLS verification. This is insecure and should not be used in production")
225+
import urllib3
226+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
205227

206228
if not GitHub.check_name(name, scope):
207229
raise ValueError("Invalid name: %s for %s", name, scope)
208230

209-
results = list_code_scanning_alerts(name, scope, hostname, state=state, since=since, raw=args.raw)
231+
results = list_code_scanning_alerts(name, scope, hostname, state=state, since=since, raw=args.raw, verify=verify)
210232

211233
if args.json:
212234
print(json.dumps(list(results), indent=2))

list_secret_scanning_alerts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ def main() -> None:
375375
verify = True
376376

377377
if args.ca_cert_bundle:
378-
verify = ca_cert_bundle
378+
verify = args.ca_cert_bundle
379379

380380
if args.no_verify_tls:
381381
verify = False

replay_code_scanning_alert_status.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ def existing_results_by_location(reader: csv.DictReader) -> dict:
5050
return existing_results
5151

5252

53-
def change_state(hostname, result: dict, res: dict) -> None:
53+
def change_state(hostname, result: dict, res: dict, verify: bool | str = True) -> None:
5454
"""Change the state of the alert to match the existing result using the GitHub API to update the alert."""
55-
g = GitHub(hostname=hostname)
55+
g = GitHub(hostname=hostname, verify=verify)
5656

5757
repo_name = result["repo"]
5858

@@ -77,7 +77,7 @@ def change_state(hostname, result: dict, res: dict) -> None:
7777
return
7878

7979

80-
def update_states(hostname: str, results: Iterable[dict], existing_results: dict) -> None:
80+
def update_states(hostname: str, results: Iterable[dict], existing_results: dict, verify: bool | str = True) -> None:
8181
"""Update the state of matching alerts to match the existing results."""
8282
for result in results:
8383
repo = result["repo"]
@@ -101,7 +101,7 @@ def update_states(hostname: str, results: Iterable[dict], existing_results: dict
101101
if res["state"] != result["state"]:
102102
LOG.warning(f"State mismatch: {res['state']} != {result['state']}")
103103

104-
change_state(hostname, result, res)
104+
change_state(hostname, result, res, verify=verify)
105105

106106

107107
def add_args(parser: argparse.ArgumentParser) -> None:
@@ -145,6 +145,18 @@ def add_args(parser: argparse.ArgumentParser) -> None:
145145
required=False,
146146
help="GitHub Enterprise hostname (defaults to github.com)",
147147
)
148+
parser.add_argument(
149+
"--ca-cert-bundle",
150+
"-C",
151+
type=str,
152+
required=False,
153+
help="Path to CA certificate bundle in PEM format (e.g. for self-signed server certificates)"
154+
)
155+
parser.add_argument(
156+
"--no-verify-tls",
157+
action="store_true",
158+
help="Do not verify TLS connection certificates (warning: insecure)"
159+
)
148160
parser.add_argument(
149161
"--debug", "-d", action="store_true", help="Enable debug logging"
150162
)
@@ -166,6 +178,16 @@ def main() -> None:
166178
name = args.name
167179
state = args.state
168180
hostname = args.hostname
181+
verify = True
182+
183+
if args.ca_cert_bundle:
184+
verify = args.ca_cert_bundle
185+
186+
if args.no_verify_tls:
187+
verify = False
188+
LOG.warning("Disabling TLS verification. This is insecure and should not be used in production")
189+
import urllib3
190+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
169191

170192
if not GitHub.check_name(args.name, scope):
171193
raise ValueError("Invalid name: %s for %s", args.name, scope)
@@ -179,9 +201,9 @@ def main() -> None:
179201

180202
LOG.debug(existing_results)
181203

182-
results = list_code_scanning_alerts(name, scope, hostname, state=state, since=since)
204+
results = list_code_scanning_alerts(name, scope, hostname, state=state, since=since, verify=verify)
183205

184-
update_states(hostname, results, existing_results)
206+
update_states(hostname, results, existing_results, verify=verify)
185207

186208

187209
if __name__ == "__main__":

replay_secret_scanning_result_status.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ def existing_results_by_secret(reader: csv.DictReader) -> dict:
4444
return existing_results
4545

4646

47-
def change_state(hostname, result: dict, res: dict) -> None:
47+
def change_state(hostname, result: dict, res: dict, verify: bool | str = True) -> None:
4848
"""Change the state of the alert to match the existing result using the GitHub API to update the alert."""
49-
g = GitHub(hostname=hostname)
49+
g = GitHub(hostname=hostname, verify=verify)
5050

5151
repo_name = result["repo"]
5252

@@ -112,6 +112,18 @@ def add_args(parser: argparse.ArgumentParser) -> None:
112112
required=False,
113113
help="GitHub Enterprise hostname (defaults to github.com)",
114114
)
115+
parser.add_argument(
116+
"--ca-cert-bundle",
117+
"-C",
118+
type=str,
119+
required=False,
120+
help="Path to CA certificate bundle in PEM format (e.g. for self-signed server certificates)"
121+
)
122+
parser.add_argument(
123+
"--no-verify-tls",
124+
action="store_true",
125+
help="Do not verify TLS connection certificates (warning: insecure)"
126+
)
115127
parser.add_argument(
116128
"--debug", "-d", action="store_true", help="Enable debug logging"
117129
)
@@ -133,6 +145,16 @@ def main() -> None:
133145
name = args.name
134146
state = args.state
135147
hostname = args.hostname
148+
verify = True
149+
150+
if args.ca_cert_bundle:
151+
verify = args.ca_cert_bundle
152+
153+
if args.no_verify_tls:
154+
verify = False
155+
LOG.warning("Disabling TLS verification. This is insecure and should not be used in production")
156+
import urllib3
157+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
136158

137159
if not GitHub.check_name(args.name, scope):
138160
raise ValueError("Invalid name: %s for %s", args.name, scope)
@@ -146,7 +168,7 @@ def main() -> None:
146168

147169
LOG.debug(existing_results)
148170

149-
results = list_secret_scanning_alerts(name, scope, hostname, state=state, since=since)
171+
results = list_secret_scanning_alerts(name, scope, hostname, state=state, since=since, verify=verify)
150172

151173
for result in results:
152174
repo = result["repo"]
@@ -165,7 +187,7 @@ def main() -> None:
165187
LOG.warning(f"State mismatch: {res['state']} != {result['state']}")
166188

167189
if result["state"] != "pattern_edited":
168-
change_state(hostname, result, res)
190+
change_state(hostname, result, res, verify=verify)
169191

170192

171193
if __name__ == "__main__":

0 commit comments

Comments
 (0)