securenow 5.17.1 → 6.0.0
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/LICENSE +15 -0
- package/README.md +40 -243
- package/cli.js +455 -425
- package/console-instrumentation.js +136 -147
- package/docs/ALL-FRAMEWORKS-QUICKSTART.md +455 -1339
- package/docs/ARCHITECTURE.md +3 -3
- package/docs/AUTO-BODY-CAPTURE.md +1 -1
- package/docs/AUTO-SETUP.md +4 -4
- package/docs/AUTOMATIC-IP-CAPTURE.md +5 -5
- package/docs/BODY-CAPTURE-QUICKSTART.md +2 -2
- package/docs/CHANGELOG-NEXTJS.md +1 -1
- package/docs/CUSTOMER-GUIDE.md +16 -16
- package/docs/EASIEST-SETUP.md +5 -5
- package/docs/ENVIRONMENT-VARIABLES.md +652 -880
- package/docs/EXPRESS-BODY-CAPTURE.md +12 -13
- package/docs/EXPRESS-SETUP-GUIDE.md +720 -719
- package/docs/INDEX.md +4 -22
- package/docs/LOGGING-GUIDE.md +708 -701
- package/docs/LOGGING-QUICKSTART.md +239 -234
- package/docs/NEXTJS-BODY-CAPTURE.md +2 -2
- package/docs/NEXTJS-GUIDE.md +14 -14
- package/docs/NEXTJS-QUICKSTART.md +1 -1
- package/docs/NEXTJS-WRAPPER-APPROACH.md +1 -1
- package/docs/QUICKSTART-BODY-CAPTURE.md +2 -2
- package/docs/REDACTION-EXAMPLES.md +1 -1
- package/docs/REQUEST-BODY-CAPTURE.md +10 -19
- package/docs/VERCEL-OTEL-MIGRATION.md +3 -3
- package/examples/README.md +6 -6
- package/examples/instrumentation-with-auto-capture.ts +1 -1
- package/examples/nextjs-env-example.txt +2 -2
- package/examples/nextjs-instrumentation.js +1 -1
- package/examples/nextjs-instrumentation.ts +1 -1
- package/examples/nextjs-with-logging-example.md +6 -6
- package/examples/nextjs-with-options.ts +1 -1
- package/examples/test-nextjs-setup.js +1 -1
- package/nextjs-auto-capture.js +207 -199
- package/nextjs-middleware.js +181 -186
- package/nextjs-webpack-config.js +53 -88
- package/nextjs-wrapper.js +158 -158
- package/nextjs.d.ts +1 -1
- package/nextjs.js +135 -190
- package/package.json +45 -67
- package/postinstall.js +6 -6
- package/register.d.ts +1 -1
- package/register.js +4 -39
- package/tracing.d.ts +1 -2
- package/tracing.js +22 -287
- package/web-vite.mjs +156 -239
- package/CONSUMING-APPS-GUIDE.md +0 -455
- package/NPM_README.md +0 -1958
- package/SKILL-API.md +0 -600
- package/SKILL-CLI.md +0 -419
- package/cidr.js +0 -83
- package/cli/apps.js +0 -585
- package/cli/auth.js +0 -280
- package/cli/client.js +0 -115
- package/cli/config.js +0 -173
- package/cli/firewall.js +0 -100
- package/cli/fp.js +0 -638
- package/cli/init.js +0 -201
- package/cli/monitor.js +0 -545
- package/cli/run.js +0 -133
- package/cli/security.js +0 -1064
- package/cli/ui.js +0 -386
- package/docs/API-KEYS-GUIDE.md +0 -233
- package/docs/AUTO-SETUP-SUMMARY.md +0 -331
- package/docs/BODY-CAPTURE-FIX.md +0 -261
- package/docs/COMPLETION-REPORT.md +0 -408
- package/docs/FINAL-SOLUTION.md +0 -335
- package/docs/FIREWALL-GUIDE.md +0 -426
- package/docs/IMPLEMENTATION-SUMMARY.md +0 -410
- package/docs/NEXTJS-BODY-CAPTURE-COMPARISON.md +0 -323
- package/docs/NEXTJS-SETUP-COMPLETE.md +0 -795
- package/docs/NUXT-GUIDE.md +0 -166
- package/docs/SOLUTION-SUMMARY.md +0 -312
- package/firewall-cloud.js +0 -212
- package/firewall-iptables.js +0 -139
- package/firewall-only.js +0 -38
- package/firewall-tcp.js +0 -74
- package/firewall.js +0 -720
- package/free-trial-banner.js +0 -174
- package/nuxt-server-plugin.mjs +0 -423
- package/nuxt.d.ts +0 -60
- package/nuxt.mjs +0 -75
- package/resolve-ip.js +0 -77
package/firewall-iptables.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Layer 3: OS-level firewall via iptables/nftables.
|
|
5
|
-
* Manages a dedicated SECURENOW_BLOCK chain — never touches customer rules.
|
|
6
|
-
* Linux only, requires root or CAP_NET_ADMIN. Falls back gracefully otherwise.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { execSync } = require('child_process');
|
|
10
|
-
const os = require('os');
|
|
11
|
-
|
|
12
|
-
const CHAIN_NAME = 'SECURENOW_BLOCK';
|
|
13
|
-
|
|
14
|
-
let _options = null;
|
|
15
|
-
let _active = false;
|
|
16
|
-
let _useNft = false;
|
|
17
|
-
|
|
18
|
-
function exec(cmd) {
|
|
19
|
-
return execSync(cmd, { stdio: 'pipe', timeout: 10000 }).toString().trim();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function canRun(cmd) {
|
|
23
|
-
try { exec(cmd); return true; } catch { return false; }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function detectBackend() {
|
|
27
|
-
if (canRun('nft --version')) return 'nft';
|
|
28
|
-
if (canRun('iptables --version')) return 'iptables';
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// ────── iptables backend ──────
|
|
33
|
-
|
|
34
|
-
function iptablesSetup() {
|
|
35
|
-
try { exec(`iptables -N ${CHAIN_NAME}`); } catch (_) {} // chain may already exist
|
|
36
|
-
try { exec(`iptables -C INPUT -j ${CHAIN_NAME}`); } catch (_) {
|
|
37
|
-
exec(`iptables -I INPUT 1 -j ${CHAIN_NAME}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function iptablesSync(ips) {
|
|
42
|
-
exec(`iptables -F ${CHAIN_NAME}`);
|
|
43
|
-
for (const ip of ips) {
|
|
44
|
-
exec(`iptables -A ${CHAIN_NAME} -s ${ip} -j DROP`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function iptablesCleanup() {
|
|
49
|
-
try { exec(`iptables -D INPUT -j ${CHAIN_NAME}`); } catch (_) {}
|
|
50
|
-
try { exec(`iptables -F ${CHAIN_NAME}`); } catch (_) {}
|
|
51
|
-
try { exec(`iptables -X ${CHAIN_NAME}`); } catch (_) {}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ────── nftables backend ──────
|
|
55
|
-
|
|
56
|
-
function nftSetup() {
|
|
57
|
-
try { exec(`nft list chain ip filter ${CHAIN_NAME}`); } catch (_) {
|
|
58
|
-
exec(`nft add chain ip filter ${CHAIN_NAME}`);
|
|
59
|
-
}
|
|
60
|
-
try { exec(`nft insert rule ip filter INPUT jump ${CHAIN_NAME}`); } catch (_) {}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function nftSync(ips) {
|
|
64
|
-
exec(`nft flush chain ip filter ${CHAIN_NAME}`);
|
|
65
|
-
for (const ip of ips) {
|
|
66
|
-
exec(`nft add rule ip filter ${CHAIN_NAME} ip saddr ${ip} drop`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function nftCleanup() {
|
|
71
|
-
try { exec(`nft flush chain ip filter ${CHAIN_NAME}`); } catch (_) {}
|
|
72
|
-
try { exec(`nft delete chain ip filter ${CHAIN_NAME}`); } catch (_) {}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// ────── Public API ──────
|
|
76
|
-
|
|
77
|
-
let _syncCallback = null;
|
|
78
|
-
|
|
79
|
-
function init(options) {
|
|
80
|
-
_options = options;
|
|
81
|
-
|
|
82
|
-
if (os.platform() !== 'linux') {
|
|
83
|
-
if (_options.log) console.warn('[securenow] Firewall iptables: only supported on Linux, skipping');
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const backend = detectBackend();
|
|
88
|
-
if (!backend) {
|
|
89
|
-
if (_options.log) console.warn('[securenow] Firewall iptables: neither iptables nor nft found, skipping');
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
_useNft = backend === 'nft';
|
|
94
|
-
const label = _useNft ? 'nftables' : 'iptables';
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
if (_useNft) nftSetup(); else iptablesSetup();
|
|
98
|
-
_active = true;
|
|
99
|
-
if (_options.log) console.log('[securenow] Firewall iptables: %s chain %s ready', label, CHAIN_NAME);
|
|
100
|
-
} catch (e) {
|
|
101
|
-
if (_options.log) console.warn('[securenow] Firewall iptables: setup failed (need root or CAP_NET_ADMIN):', e.message);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Called by the firewall core after each successful blocklist sync.
|
|
107
|
-
* @param {string[]} ips - Array of IPs and CIDRs to block
|
|
108
|
-
*/
|
|
109
|
-
function sync(ips) {
|
|
110
|
-
if (!_active) return;
|
|
111
|
-
|
|
112
|
-
const maxRules = (_options && _options.iptablesMax) || 10000;
|
|
113
|
-
const limited = ips.length > maxRules ? ips.slice(0, maxRules) : ips;
|
|
114
|
-
|
|
115
|
-
if (ips.length > maxRules && _options.log) {
|
|
116
|
-
console.warn('[securenow] Firewall iptables: truncated to %d rules (max %d)', maxRules, maxRules);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
if (_useNft) nftSync(limited); else iptablesSync(limited);
|
|
121
|
-
} catch (e) {
|
|
122
|
-
if (_options && _options.log) {
|
|
123
|
-
console.warn('[securenow] Firewall iptables: sync failed:', e.message);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function shutdown() {
|
|
129
|
-
if (!_active) return;
|
|
130
|
-
try {
|
|
131
|
-
if (_useNft) nftCleanup(); else iptablesCleanup();
|
|
132
|
-
if (_options && _options.log) console.log('[securenow] Firewall iptables: chain %s cleaned up', CHAIN_NAME);
|
|
133
|
-
} catch (e) {
|
|
134
|
-
if (_options && _options.log) console.warn('[securenow] Firewall iptables: cleanup failed:', e.message);
|
|
135
|
-
}
|
|
136
|
-
_active = false;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
module.exports = { init, sync, shutdown };
|
package/firewall-only.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Standalone firewall preload — no OpenTelemetry, no tracing.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* node -r securenow/firewall-only app.js
|
|
8
|
-
* NODE_OPTIONS='-r securenow/firewall-only' next start
|
|
9
|
-
*
|
|
10
|
-
* Reads .env via dotenv (if installed), then initialises the HTTP-level
|
|
11
|
-
* firewall when SECURENOW_API_KEY is present.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
try { require('dotenv').config(); } catch (_) {}
|
|
15
|
-
|
|
16
|
-
const env = (k) =>
|
|
17
|
-
process.env[k] ?? process.env[k.toUpperCase()] ?? process.env[k.toLowerCase()];
|
|
18
|
-
|
|
19
|
-
const firewallApiKey = env('SECURENOW_API_KEY');
|
|
20
|
-
|
|
21
|
-
if (firewallApiKey && env('SECURENOW_FIREWALL_ENABLED') !== '0') {
|
|
22
|
-
require('./firewall').init({
|
|
23
|
-
apiKey: firewallApiKey,
|
|
24
|
-
apiUrl: env('SECURENOW_API_URL') || 'https://api.securenow.ai',
|
|
25
|
-
versionCheckInterval: parseInt(env('SECURENOW_FIREWALL_VERSION_INTERVAL'), 10) || 10,
|
|
26
|
-
syncInterval: parseInt(env('SECURENOW_FIREWALL_SYNC_INTERVAL'), 10) || 300,
|
|
27
|
-
failMode: env('SECURENOW_FIREWALL_FAIL_MODE') || 'open',
|
|
28
|
-
statusCode: parseInt(env('SECURENOW_FIREWALL_STATUS_CODE'), 10) || 403,
|
|
29
|
-
log: env('SECURENOW_FIREWALL_LOG') !== '0',
|
|
30
|
-
tcp: env('SECURENOW_FIREWALL_TCP') === '1',
|
|
31
|
-
iptables: env('SECURENOW_FIREWALL_IPTABLES') === '1',
|
|
32
|
-
cloud: env('SECURENOW_FIREWALL_CLOUD') || null,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const shutdown = () => { try { require('./firewall').shutdown(); } catch (_) {} };
|
|
36
|
-
process.on('SIGINT', shutdown);
|
|
37
|
-
process.on('SIGTERM', shutdown);
|
|
38
|
-
}
|
package/firewall-tcp.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Layer 2: TCP-level blocking via net.Server connection event.
|
|
5
|
-
* Destroys the socket before HTTP parsing starts. Zero bytes sent back.
|
|
6
|
-
*
|
|
7
|
-
* Caveat: only sees socket.remoteAddress (direct connection IP).
|
|
8
|
-
* If behind a reverse proxy, the proxy IP is seen instead.
|
|
9
|
-
* Proxy IPs are skipped (let through to Layer 1 for proper header-based resolution).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const net = require('net');
|
|
13
|
-
const { resolveSocketIp, isFromTrustedProxy } = require('./resolve-ip');
|
|
14
|
-
|
|
15
|
-
let _getMatcher = null;
|
|
16
|
-
let _getAllowlistMatcher = null;
|
|
17
|
-
let _options = null;
|
|
18
|
-
let _patched = false;
|
|
19
|
-
const _origListen = net.Server.prototype.listen;
|
|
20
|
-
|
|
21
|
-
function onConnection(socket) {
|
|
22
|
-
const ip = resolveSocketIp(socket);
|
|
23
|
-
|
|
24
|
-
// Skip if the connection is from a trusted proxy — Layer 1 will handle it
|
|
25
|
-
// with proper X-Forwarded-For resolution
|
|
26
|
-
if (isFromTrustedProxy(ip) || isFromTrustedProxy('::ffff:' + ip)) return;
|
|
27
|
-
|
|
28
|
-
// Allowlist check: if active, only listed IPs pass
|
|
29
|
-
const allowlistMatcher = _getAllowlistMatcher ? _getAllowlistMatcher() : null;
|
|
30
|
-
if (allowlistMatcher && allowlistMatcher.stats().total > 0) {
|
|
31
|
-
if (!allowlistMatcher.isBlocked(ip)) {
|
|
32
|
-
if (_options && _options.log) {
|
|
33
|
-
console.log('[securenow] Firewall: blocked %s via TCP (not in allowlist)', ip);
|
|
34
|
-
}
|
|
35
|
-
socket.destroy();
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
return; // on allowlist — allow
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Blocklist check
|
|
42
|
-
const matcher = _getMatcher();
|
|
43
|
-
if (!matcher) return;
|
|
44
|
-
|
|
45
|
-
if (matcher.isBlocked(ip)) {
|
|
46
|
-
if (_options && _options.log) {
|
|
47
|
-
console.log('[securenow] Firewall: blocked %s via TCP (socket destroyed)', ip);
|
|
48
|
-
}
|
|
49
|
-
socket.destroy();
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function init(getMatcher, options, getAllowlistMatcher) {
|
|
54
|
-
_getMatcher = getMatcher;
|
|
55
|
-
_getAllowlistMatcher = getAllowlistMatcher || null;
|
|
56
|
-
_options = options;
|
|
57
|
-
|
|
58
|
-
if (_patched) return;
|
|
59
|
-
_patched = true;
|
|
60
|
-
|
|
61
|
-
net.Server.prototype.listen = function(...args) {
|
|
62
|
-
this.on('connection', onConnection);
|
|
63
|
-
return _origListen.apply(this, args);
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function shutdown() {
|
|
68
|
-
if (_patched) {
|
|
69
|
-
net.Server.prototype.listen = _origListen;
|
|
70
|
-
_patched = false;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
module.exports = { init, shutdown };
|