Cion-lab commited on
Commit
a4b475b
·
verified ·
1 Parent(s): dc56217

p1 secret bootstrap: privacy-gated, placeholder-first ordering

Browse files
Files changed (1) hide show
  1. probes/p1_secret_bootstrap.py +145 -0
probes/p1_secret_bootstrap.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Phase 1 bootstrap worker: create a PRIVATE Kaggle dataset to hold the HF token, safely.
2
+ #
3
+ # Why this exists (closes D-002). §2 permits the token to come "from the environment or a secret
4
+ # store" and forbids it reaching a public artifact. p1_credential_path proved the Kaggle CLI is
5
+ # preinstalled and already authenticated inside an internet-enabled session, so a job can create its
6
+ # own private dataset and later jobs can mount it from /kaggle/input instead of carrying the token in
7
+ # their source.
8
+ #
9
+ # THE ORDERING HERE IS A SECURITY CONTROL, NOT STYLE. `kaggle datasets create` decides visibility
10
+ # from a metadata field whose name has varied between CLI versions (isPrivate vs defineIsPrivate), and
11
+ # an unrecognised key is silently ignored -- which would create a PUBLIC dataset. A public dataset
12
+ # containing the token would be an unrecoverable leak, so:
13
+ #
14
+ # 1. create with an inert placeholder file only,
15
+ # 2. PROVE it is private by failing to download that file anonymously,
16
+ # 3. only then push the real credential as a new version,
17
+ # 4. re-prove, and delete nothing that was not created here.
18
+ #
19
+ # If step 2 ever shows anonymous read succeeding, the secret is never uploaded and the job says so.
20
+ # No token value is ever printed; kernel stdout is retained. Zero GPU quota (CPU session).
21
+
22
+ import hashlib
23
+ import json
24
+ import os
25
+ import subprocess
26
+ import time
27
+ import urllib.error
28
+ import urllib.request
29
+
30
+ R = {}
31
+ DS_SLUG = "ounce100m-secret-store"
32
+ DS_ID = f"dodosoomro/{DS_SLUG}"
33
+ TOKEN_ENV = "OUNCE100M_HF_TOKEN"
34
+ DS_DIR = "/kaggle/working/ds"
35
+ PLACEHOLDER = "placeholder.txt"
36
+ SECRET_FILE = "credentials.json"
37
+
38
+
39
+ def scrub(s):
40
+ import re
41
+
42
+ s = s if isinstance(s, str) else str(s)
43
+ s = re.sub(r"hf_[A-Za-z0-9]{16,}", "<REDACTED_TOKEN>", s)
44
+ return re.sub(r"[A-Za-z0-9_\-]{36,}", "<REDACTED_LONG>", s)[:1200]
45
+
46
+
47
+ def run(cmd, timeout=300):
48
+ p = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
49
+ return {"rc": p.returncode, "out": scrub(p.stdout), "err": scrub(p.stderr)}
50
+
51
+
52
+ def anon_can_read(path):
53
+ """True only if an unauthenticated request can fetch the file. That means PUBLIC -- bad."""
54
+ url = f"https://www.kaggle.com/api/v1/datasets/download/{DS_ID}"
55
+ try:
56
+ with urllib.request.urlopen(url, timeout=60) as r:
57
+ return {"readable": True, "status": r.status, "bytes": len(r.read(2048))}
58
+ except urllib.error.HTTPError as e:
59
+ return {"readable": False, "status": e.code, "reason": str(e.reason)[:80]}
60
+ except Exception as e:
61
+ return {"readable": None, "error": f"{type(e).__name__}: {e}"[:160]}
62
+
63
+
64
+ def write_metadata(private=True):
65
+ """Both spellings of the visibility key, because the CLI version in the image is 2.0.2 and the
66
+ field name has moved across releases; an unrecognised key is ignored without error."""
67
+ md = {
68
+ "title": "ounce100m runtime assets",
69
+ "id": DS_ID,
70
+ "resources": [{"path": PLACEHOLDER, "description": "bootstrap placeholder",
71
+ "resourceName": "placeholder", "resourceType": "dataset"}],
72
+ }
73
+ if private:
74
+ md["isPrivate"] = True
75
+ md["defineIsPrivate"] = True
76
+ with open(os.path.join(DS_DIR, "dataset-metadata.json"), "w") as f:
77
+ json.dump(md, f)
78
+
79
+
80
+ def write_files(with_secret):
81
+ os.makedirs(DS_DIR, exist_ok=True)
82
+ with open(os.path.join(DS_DIR, PLACEHOLDER), "w") as f:
83
+ f.write("ounce100m bootstrap placeholder\n")
84
+ if with_secret:
85
+ tok = os.environ[TOKEN_ENV]
86
+ with open(os.path.join(DS_DIR, SECRET_FILE), "w") as f:
87
+ json.dump({"HF_TOKEN": tok}, f)
88
+ os.chmod(os.path.join(DS_DIR, SECRET_FILE), 0o600)
89
+
90
+
91
+ tok = os.environ.get(TOKEN_ENV, "")
92
+ R["pre"] = {"token_present": bool(tok), "token_length": len(tok),
93
+ "token_sha256_prefix": hashlib.sha256(tok.encode()).hexdigest()[:12] if tok else None}
94
+
95
+ if not tok:
96
+ R["fatal"] = f"{TOKEN_ENV} unset; nothing created"
97
+ else:
98
+ write_files(with_secret=False)
99
+ write_metadata(private=True)
100
+
101
+ existing = run(f"kaggle datasets status {DS_ID} 2>&1", 120)
102
+ R["pre_existing_status"] = existing
103
+ already = existing["rc"] == 0
104
+
105
+ if not already:
106
+ t = time.monotonic()
107
+ R["create"] = run(f"kaggle datasets create -p {DS_DIR} -q 2>&1", timeout=600)
108
+ R["create"]["seconds"] = round(time.monotonic() - t, 1)
109
+ time.sleep(12) # visibility is applied asynchronously; an immediate probe can lie
110
+
111
+ # ---- the gate: prove privacy BEFORE any secret exists on the dataset
112
+ privacy = anon_can_read(PLACEHOLDER)
113
+ R["privacy_check_placeholder_only"] = privacy
114
+ R["private_confirmed"] = privacy.get("readable") is False
115
+
116
+ if R["private_confirmed"]:
117
+ write_files(with_secret=True)
118
+ t = time.monotonic()
119
+ R["version_with_secret"] = run(
120
+ f'kaggle datasets version -p {DS_DIR} -m "provision runtime credential" -q 2>&1',
121
+ timeout=600)
122
+ R["version_with_secret"]["seconds"] = round(time.monotonic() - t, 1)
123
+ time.sleep(12)
124
+ after = anon_can_read(SECRET_FILE)
125
+ R["privacy_check_after_secret"] = after
126
+ R["secret_still_private"] = after.get("readable") is False
127
+ if not R["secret_still_private"]:
128
+ R["EMERGENCY"] = ("Anonymous read succeeded AFTER the credential was uploaded -- the "
129
+ "dataset may be public. Delete dodosoomro/ounce100m-secret-store from "
130
+ "the Kaggle web UI immediately and rotate the HF token.")
131
+ else:
132
+ R["refused_to_upload"] = ("Dataset was anonymously readable while holding only a placeholder; "
133
+ "the secret was NOT uploaded. Fix visibility handling before retry.")
134
+
135
+ R["files"] = run(f"kaggle datasets files {DS_ID} 2>&1", 120)
136
+ if R.get("secret_still_private"):
137
+ R["mount_hint_for_future_jobs"] = {
138
+ "datasetDataSources": [DS_ID],
139
+ "read_from": f"/kaggle/input/{DS_SLUG}/{SECRET_FILE}",
140
+ "then": "json.load(...)['HF_TOKEN'] -> os.environ['HF_TOKEN']; never print the value",
141
+ }
142
+
143
+ print("PROBE_JSON_BEGIN")
144
+ print(json.dumps(R, indent=1, default=str))
145
+ print("PROBE_JSON_END")