securenow 5.10.2 → 5.11.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/cli.js CHANGED
@@ -119,6 +119,32 @@ const COMMANDS = {
119
119
  },
120
120
  defaultSub: 'rules',
121
121
  },
122
+ fp: {
123
+ desc: 'Manage false-positive exclusion rules',
124
+ usage: 'securenow fp <subcommand> [options]',
125
+ flags: { json: 'Output as JSON', conditions: 'JSON array of conditions', 'match-mode': 'Match mode: all (AND) or any (OR)', 'rule-scope': 'Scope: this_rule | specific_rules | all_existing | any_rule', 'target-rules': 'Comma-separated rule IDs (when --rule-scope specific_rules)', 'path-safe': 'Add path_safe_values condition (standard|strict)', 'query-safe': 'Add query_safe_values condition (standard|strict)', 'query-keys': 'Allowed query param names (comma-separated)', 'ua-safe': 'Add ua_safe_values condition (standard|strict)', 'headers-safe': 'Add headers_safe_values condition (standard|strict)', 'headers-keys': 'Allowed header names (comma-separated)' },
126
+ sub: {
127
+ list: { desc: 'List all exclusion rules', run: (a, f) => require('./cli/fp').list(a, f) },
128
+ show: { desc: 'Show exclusion rule details', usage: 'securenow fp show <id>', run: (a, f) => require('./cli/fp').show(a, f) },
129
+ create: { desc: 'Create an exclusion rule', usage: 'securenow fp create [--conditions \'[...]\'] [--path /api/event] [--method POST] [--path-safe standard] [--ua-safe standard] [--headers-safe standard] [--query-keys page,limit] [--headers-keys host,content-type] [--reason "..."] [--rule-scope any_rule|specific_rules|all_existing] [--target-rules id1,id2]', run: (a, f) => require('./cli/fp').create(a, f) },
130
+ edit: { desc: 'Edit an exclusion rule', usage: 'securenow fp edit <id> [--active true/false] [--conditions \'[...]\']', run: (a, f) => require('./cli/fp').edit(a, f) },
131
+ delete: { desc: 'Delete an exclusion rule', usage: 'securenow fp delete <id> [--yes]', run: (a, f) => require('./cli/fp').remove(a, f) },
132
+ 'test-body': { desc: 'Test a request body against conditions', usage: 'securenow fp test-body <body|@file> --conditions \'[...]\'', run: (a, f) => require('./cli/fp').testBody(a, f) },
133
+ 'dry-run': { desc: 'Dry-run conditions against live traces (last 3 days)', usage: 'securenow fp dry-run --conditions \'[...]\'', run: (a, f) => require('./cli/fp').dryRun(a, f) },
134
+ 'ai-fill': { desc: 'AI-generate exclusion conditions', usage: 'securenow fp ai-fill [--description "..."] [--context \'{"method":"POST",...}\']', run: (a, f) => require('./cli/fp').aiFill(a, f) },
135
+ mark: { desc: 'Mark an IP as false positive on a notification', usage: 'securenow fp mark <notification-id> <ip> [--conditions \'[...]\'] [--reason "..."] [--rule-scope this_rule|specific_rules|all_existing|any_rule] [--target-rules id1,id2]', run: (a, f) => require('./cli/fp').mark(a, f) },
136
+ },
137
+ defaultSub: 'list',
138
+ },
139
+ firewall: {
140
+ desc: 'Firewall status and IP testing',
141
+ usage: 'securenow firewall <subcommand> [options]',
142
+ sub: {
143
+ status: { desc: 'Show firewall status, layers, and blocklist info', run: (a, f) => require('./cli/firewall').status(a, f) },
144
+ 'test-ip': { desc: 'Check if an IP would be blocked', usage: 'securenow firewall test-ip <ip>', run: (a, f) => require('./cli/firewall').testIp(a, f) },
145
+ },
146
+ defaultSub: 'status',
147
+ },
122
148
  blocklist: {
123
149
  desc: 'Manage IP blocklist',
124
150
  usage: 'securenow blocklist <subcommand> [options]',
@@ -305,8 +331,9 @@ function showHelp(commandName) {
305
331
  'Authentication': ['login', 'logout', 'whoami'],
306
332
  'Applications': ['apps', 'init', 'status'],
307
333
  'Observe': ['traces', 'logs', 'analytics'],
308
- 'Detect & Respond': ['issues', 'notifications', 'alerts'],
334
+ 'Detect & Respond': ['issues', 'notifications', 'alerts', 'fp'],
309
335
  'Investigate': ['ip', 'forensics', 'api-map'],
336
+ 'Firewall': ['firewall'],
310
337
  'Remediation': ['blocklist', 'trusted'],
311
338
  'Settings': ['instances', 'config', 'version'],
312
339
  };
@@ -1,147 +1,147 @@
1
- 'use strict';
2
-
3
- /**
4
- * Console instrumentation helper for securenow
5
- *
6
- * This module wraps the default console methods (log, info, warn, error, debug)
7
- * to automatically send logs to OpenTelemetry / any OTLP-compatible backend.
8
- *
9
- * Usage:
10
- * 1. Enable logging: SECURENOW_LOGGING_ENABLED=1
11
- * 2. Import this file AFTER securenow is initialized
12
- * 3. Use console.log/info/warn/error as normal
13
- *
14
- * Example:
15
- * // At the top of your app.js or index.js
16
- * require('securenow/register'); // or use NODE_OPTIONS
17
- * require('securenow/console-instrumentation');
18
- *
19
- * // Now all console calls are captured
20
- * console.log('Application started');
21
- * console.error('An error occurred');
22
- */
23
-
24
- const tracing = require('./tracing');
25
-
26
- if (!tracing.isLoggingEnabled()) {
27
- console.warn('[securenow] Console instrumentation loaded but logging is not enabled. Set SECURENOW_LOGGING_ENABLED=1 to enable.');
28
- }
29
-
30
- // Get a logger instance
31
- const logger = tracing.getLogger('console', '1.0.0');
32
-
33
- if (!logger) {
34
- console.warn('[securenow] Console instrumentation: No logger available. Logging will not work.');
35
- module.exports = {};
36
- return;
37
- }
38
-
39
- if (console.__securenow_patched) {
40
- console.warn('[securenow] Console already instrumented by tracing.js — skipping to avoid duplicate logs.');
41
- module.exports = {};
42
- return;
43
- }
44
-
45
- // Store original console methods
46
- const originalConsole = {
47
- log: console.log,
48
- info: console.info,
49
- warn: console.warn,
50
- error: console.error,
51
- debug: console.debug,
52
- };
53
-
54
- // Map severity levels (OpenTelemetry standard)
55
- const SeverityNumber = {
56
- DEBUG: 5,
57
- INFO: 9,
58
- WARN: 13,
59
- ERROR: 17,
60
- };
61
-
62
- /**
63
- * Format arguments into a log message
64
- */
65
- function formatMessage(args) {
66
- return args
67
- .map((arg) => {
68
- if (typeof arg === 'object' && arg !== null) {
69
- try {
70
- return JSON.stringify(arg);
71
- } catch (e) {
72
- return String(arg);
73
- }
74
- }
75
- return String(arg);
76
- })
77
- .join(' ');
78
- }
79
-
80
- const { context, trace } = require('@opentelemetry/api');
81
-
82
- /**
83
- * Emit a log record, correlating with the active trace/span when available
84
- */
85
- function emitLog(severityNumber, severityText, args) {
86
- const message = formatMessage(args);
87
-
88
- try {
89
- const activeCtx = context.active();
90
- const spanCtx = trace.getSpanContext(activeCtx);
91
- logger.emit({
92
- severityNumber,
93
- severityText,
94
- body: message,
95
- attributes: {
96
- 'log.source': 'console',
97
- 'log.method': severityText.toLowerCase(),
98
- },
99
- ...(spanCtx && { context: activeCtx }),
100
- });
101
- } catch (e) {
102
- // Silently fail to avoid breaking the application
103
- }
104
- }
105
-
106
- // Override console.log
107
- console.log = function (...args) {
108
- emitLog(SeverityNumber.INFO, 'INFO', args);
109
- originalConsole.log.apply(console, args);
110
- };
111
-
112
- // Override console.info
113
- console.info = function (...args) {
114
- emitLog(SeverityNumber.INFO, 'INFO', args);
115
- originalConsole.info.apply(console, args);
116
- };
117
-
118
- // Override console.warn
119
- console.warn = function (...args) {
120
- emitLog(SeverityNumber.WARN, 'WARN', args);
121
- originalConsole.warn.apply(console, args);
122
- };
123
-
124
- // Override console.error
125
- console.error = function (...args) {
126
- emitLog(SeverityNumber.ERROR, 'ERROR', args);
127
- originalConsole.error.apply(console, args);
128
- };
129
-
130
- // Override console.debug
131
- console.debug = function (...args) {
132
- emitLog(SeverityNumber.DEBUG, 'DEBUG', args);
133
- originalConsole.debug.apply(console, args);
134
- };
135
-
136
- console.log('[securenow] Console instrumentation installed - all console logs will be sent to any OTLP-compatible backend');
137
-
138
- module.exports = {
139
- originalConsole,
140
- restoreConsole: () => {
141
- console.log = originalConsole.log;
142
- console.info = originalConsole.info;
143
- console.warn = originalConsole.warn;
144
- console.error = originalConsole.error;
145
- console.debug = originalConsole.debug;
146
- },
147
- };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Console instrumentation helper for securenow
5
+ *
6
+ * This module wraps the default console methods (log, info, warn, error, debug)
7
+ * to automatically send logs to OpenTelemetry / any OTLP-compatible backend.
8
+ *
9
+ * Usage:
10
+ * 1. Enable logging: SECURENOW_LOGGING_ENABLED=1
11
+ * 2. Import this file AFTER securenow is initialized
12
+ * 3. Use console.log/info/warn/error as normal
13
+ *
14
+ * Example:
15
+ * // At the top of your app.js or index.js
16
+ * require('securenow/register'); // or use NODE_OPTIONS
17
+ * require('securenow/console-instrumentation');
18
+ *
19
+ * // Now all console calls are captured
20
+ * console.log('Application started');
21
+ * console.error('An error occurred');
22
+ */
23
+
24
+ const tracing = require('./tracing');
25
+
26
+ if (!tracing.isLoggingEnabled()) {
27
+ console.warn('[securenow] Console instrumentation loaded but logging is not enabled. Set SECURENOW_LOGGING_ENABLED=1 to enable.');
28
+ }
29
+
30
+ // Get a logger instance
31
+ const logger = tracing.getLogger('console', '1.0.0');
32
+
33
+ if (!logger) {
34
+ console.warn('[securenow] Console instrumentation: No logger available. Logging will not work.');
35
+ module.exports = {};
36
+ return;
37
+ }
38
+
39
+ if (console.__securenow_patched) {
40
+ console.warn('[securenow] Console already instrumented by tracing.js — skipping to avoid duplicate logs.');
41
+ module.exports = {};
42
+ return;
43
+ }
44
+
45
+ // Store original console methods
46
+ const originalConsole = {
47
+ log: console.log,
48
+ info: console.info,
49
+ warn: console.warn,
50
+ error: console.error,
51
+ debug: console.debug,
52
+ };
53
+
54
+ // Map severity levels (OpenTelemetry standard)
55
+ const SeverityNumber = {
56
+ DEBUG: 5,
57
+ INFO: 9,
58
+ WARN: 13,
59
+ ERROR: 17,
60
+ };
61
+
62
+ /**
63
+ * Format arguments into a log message
64
+ */
65
+ function formatMessage(args) {
66
+ return args
67
+ .map((arg) => {
68
+ if (typeof arg === 'object' && arg !== null) {
69
+ try {
70
+ return JSON.stringify(arg);
71
+ } catch (e) {
72
+ return String(arg);
73
+ }
74
+ }
75
+ return String(arg);
76
+ })
77
+ .join(' ');
78
+ }
79
+
80
+ const { context, trace } = require('@opentelemetry/api');
81
+
82
+ /**
83
+ * Emit a log record, correlating with the active trace/span when available
84
+ */
85
+ function emitLog(severityNumber, severityText, args) {
86
+ const message = formatMessage(args);
87
+
88
+ try {
89
+ const activeCtx = context.active();
90
+ const spanCtx = trace.getSpanContext(activeCtx);
91
+ logger.emit({
92
+ severityNumber,
93
+ severityText,
94
+ body: message,
95
+ attributes: {
96
+ 'log.source': 'console',
97
+ 'log.method': severityText.toLowerCase(),
98
+ },
99
+ ...(spanCtx && { context: activeCtx }),
100
+ });
101
+ } catch (e) {
102
+ // Silently fail to avoid breaking the application
103
+ }
104
+ }
105
+
106
+ // Override console.log
107
+ console.log = function (...args) {
108
+ emitLog(SeverityNumber.INFO, 'INFO', args);
109
+ originalConsole.log.apply(console, args);
110
+ };
111
+
112
+ // Override console.info
113
+ console.info = function (...args) {
114
+ emitLog(SeverityNumber.INFO, 'INFO', args);
115
+ originalConsole.info.apply(console, args);
116
+ };
117
+
118
+ // Override console.warn
119
+ console.warn = function (...args) {
120
+ emitLog(SeverityNumber.WARN, 'WARN', args);
121
+ originalConsole.warn.apply(console, args);
122
+ };
123
+
124
+ // Override console.error
125
+ console.error = function (...args) {
126
+ emitLog(SeverityNumber.ERROR, 'ERROR', args);
127
+ originalConsole.error.apply(console, args);
128
+ };
129
+
130
+ // Override console.debug
131
+ console.debug = function (...args) {
132
+ emitLog(SeverityNumber.DEBUG, 'DEBUG', args);
133
+ originalConsole.debug.apply(console, args);
134
+ };
135
+
136
+ console.log('[securenow] Console instrumentation installed - all console logs will be sent to any OTLP-compatible backend');
137
+
138
+ module.exports = {
139
+ originalConsole,
140
+ restoreConsole: () => {
141
+ console.log = originalConsole.log;
142
+ console.info = originalConsole.info;
143
+ console.warn = originalConsole.warn;
144
+ console.error = originalConsole.error;
145
+ console.debug = originalConsole.debug;
146
+ },
147
+ };
@@ -24,7 +24,7 @@ Protect any Node.js app in minutes. This guide covers **installation, CLI comman
24
24
  5. [Verify It Works](#step-4--verify-it-works)
25
25
  6. [CLI Command Reference](#step-5--cli-command-reference)
26
26
  7. [Forensics Chat — Ask Questions in Plain English](#step-6--forensics-chat--ask-questions-in-plain-english)
27
- 8. [Block & Manage IPs](#step-7--block--manage-ips)
27
+ 8. [Block & Manage IPs + Firewall](#step-7--block--manage-ips)
28
28
  9. [Monitor, Detect & Respond](#step-8--monitor-detect--respond)
29
29
  10. [PM2 / Docker Deployment](#deployment)
30
30
  11. [Compatibility Matrix](#compatibility-matrix)
@@ -1087,6 +1087,45 @@ npx securenow blocklist add 185.220.101.1 --reason "brute force login attempts"
1087
1087
  npx securenow blocklist
1088
1088
  ```
1089
1089
 
1090
+ ### Enforce the Blocklist on Your App (Firewall)
1091
+
1092
+ Once you've built a blocklist, enforce it at your application layer — automatically, with zero code changes:
1093
+
1094
+ ```bash
1095
+ # Add your API key to .env
1096
+ SECURENOW_API_KEY=snk_live_abc123...
1097
+ ```
1098
+
1099
+ Restart your app. The firewall syncs the blocklist every 60 seconds and blocks matching IPs with a 403 response:
1100
+
1101
+ ```
1102
+ [securenow] Firewall: ENABLED
1103
+ [securenow] Firewall: Layer 1 (HTTP 403) active
1104
+ [securenow] Firewall: synced 142 blocked IPs
1105
+ ```
1106
+
1107
+ Enable additional layers for defense in depth:
1108
+
1109
+ ```bash
1110
+ # TCP-level blocking (zero bytes sent back)
1111
+ SECURENOW_FIREWALL_TCP=1
1112
+
1113
+ # OS-level blocking (iptables/nftables, Linux only)
1114
+ SECURENOW_FIREWALL_IPTABLES=1
1115
+
1116
+ # Cloud WAF blocking (Cloudflare, AWS WAF, GCP Cloud Armor)
1117
+ SECURENOW_FIREWALL_CLOUD=cloudflare
1118
+ ```
1119
+
1120
+ Check firewall status:
1121
+
1122
+ ```bash
1123
+ npx securenow firewall status
1124
+ npx securenow firewall test-ip 185.220.101.1
1125
+ ```
1126
+
1127
+ See the [Firewall Guide](FIREWALL-GUIDE.md) for the full reference.
1128
+
1090
1129
  ---
1091
1130
 
1092
1131
  ## Step 8 — Monitor, Detect & Respond
@@ -0,0 +1,215 @@
1
+ # SecureNow API Keys
2
+
3
+ API keys provide programmatic access to the SecureNow platform. They support granular feature-level permissions, application scoping, IP allowlisting, and secure one-time-copy generation.
4
+
5
+ ---
6
+
7
+ ## Creating an API Key
8
+
9
+ ### From the Dashboard
10
+
11
+ 1. Go to **Settings → API Keys**
12
+ 2. Click **Create API Key**
13
+ 3. Enter a name (e.g., "Production Firewall", "CI/CD Pipeline")
14
+ 4. Select the scopes (permissions) you need
15
+ 5. Optionally restrict to specific applications and IP addresses
16
+ 6. Click **Create**
17
+ 7. **Copy the key immediately** — it will only be shown once
18
+
19
+ ### From the CLI
20
+
21
+ ```bash
22
+ npx securenow login
23
+ npx securenow firewall status
24
+ ```
25
+
26
+ The `securenow init` and `securenow login` commands can automatically provision a firewall API key for you.
27
+
28
+ ---
29
+
30
+ ## Key Format
31
+
32
+ All API keys use the format:
33
+
34
+ ```
35
+ snk_live_<64 hex characters>
36
+ ```
37
+
38
+ Example: `snk_live_a1b2c3d4e5f6...`
39
+
40
+ The `snk_live_` prefix makes it easy to identify SecureNow keys in your codebase and credential scanners.
41
+
42
+ ---
43
+
44
+ ## Scopes (Permissions)
45
+
46
+ Each API key has a set of scopes that control what it can access. Scopes follow the `resource:action` pattern.
47
+
48
+ | Scope | Description |
49
+ |-------|-------------|
50
+ | `firewall:read` | Read the blocklist (used by the firewall SDK) |
51
+ | `blocklist:read` | List and check blocked IPs |
52
+ | `blocklist:write` | Add and remove blocked IPs |
53
+ | `applications:read` | List and view applications |
54
+ | `applications:write` | Create and delete applications |
55
+ | `traces:read` | Query traces |
56
+ | `logs:read` | Query logs |
57
+ | `issues:read` | List and view security issues |
58
+ | `issues:write` | Resolve and manage issues |
59
+ | `alerts:read` | View alert rules, channels, and history |
60
+ | `alerts:write` | Create and manage alert rules |
61
+ | `analytics:read` | View analytics data |
62
+ | `forensics:read` | Run forensic queries |
63
+ | `ip:read` | IP intelligence lookups |
64
+ | `trusted:read` | List trusted IPs |
65
+ | `trusted:write` | Manage trusted IPs |
66
+ | `notifications:read` | List notifications |
67
+ | `notifications:write` | Mark notifications as read |
68
+ | `api-map:read` | View API map |
69
+ | `instances:read` | List instances |
70
+ | `false-positives:read` | List false positive rules |
71
+ | `false-positives:write` | Create and manage false positive rules |
72
+
73
+ ### Principle of Least Privilege
74
+
75
+ Only grant the scopes your use case requires:
76
+
77
+ - **Firewall SDK:** `firewall:read`
78
+ - **CI/CD monitoring:** `issues:read`, `traces:read`
79
+ - **Automated remediation:** `blocklist:read`, `blocklist:write`
80
+ - **Dashboard integration:** all `*:read` scopes
81
+
82
+ ---
83
+
84
+ ## Application Scoping
85
+
86
+ Restrict an API key to specific applications. When set, the key can only access data for those applications.
87
+
88
+ Leave empty to allow access to all applications on your account.
89
+
90
+ ---
91
+
92
+ ## IP Allowlisting
93
+
94
+ Restrict an API key to specific client IPs or CIDR ranges. When set, requests from other IPs are rejected with 403.
95
+
96
+ ```
97
+ 34.56.78.90
98
+ 10.0.0.0/24
99
+ ```
100
+
101
+ Leave empty to allow from any IP.
102
+
103
+ ---
104
+
105
+ ## Using API Keys
106
+
107
+ ### In HTTP Requests
108
+
109
+ Pass the API key in the `Authorization` header:
110
+
111
+ ```bash
112
+ curl -s https://api.securenow.ai/api/v1/blocklist \
113
+ -H "Authorization: Bearer snk_live_abc123..."
114
+ ```
115
+
116
+ ### In the Firewall SDK
117
+
118
+ Set the `SECURENOW_API_KEY` environment variable:
119
+
120
+ ```bash
121
+ SECURENOW_API_KEY=snk_live_abc123...
122
+ ```
123
+
124
+ The firewall SDK reads this automatically on startup.
125
+
126
+ ### In CI/CD
127
+
128
+ ```yaml
129
+ # GitHub Actions example
130
+ env:
131
+ SECURENOW_API_KEY: ${{ secrets.SECURENOW_API_KEY }}
132
+
133
+ steps:
134
+ - run: |
135
+ ISSUES=$(curl -s https://api.securenow.ai/api/v1/issues \
136
+ -H "Authorization: Bearer $SECURENOW_API_KEY")
137
+ echo "$ISSUES" | jq '.issues | length'
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Key Management
143
+
144
+ ### Viewing Keys
145
+
146
+ ```bash
147
+ # From the CLI
148
+ npx securenow api-keys list
149
+ ```
150
+
151
+ Or go to **Settings → API Keys** in the dashboard. You'll see the key name, last 4 characters, status, scopes, and last used timestamp.
152
+
153
+ ### Revoking a Key
154
+
155
+ ```bash
156
+ npx securenow api-keys revoke <key-id>
157
+ ```
158
+
159
+ Or click **Revoke** in the dashboard. Revoked keys immediately stop working. This cannot be undone.
160
+
161
+ ### Regenerating a Key
162
+
163
+ Regeneration creates a new key with the same name, scopes, and settings. The old key is automatically revoked.
164
+
165
+ ---
166
+
167
+ ## Rate Limits
168
+
169
+ API keys are subject to rate limiting:
170
+
171
+ | Endpoint | Limit |
172
+ |----------|-------|
173
+ | General API (`/api/*`) | 600 requests/minute |
174
+ | Firewall sync (`/api/firewall/*`) | 120 requests/minute |
175
+
176
+ Rate limit headers are included in every response:
177
+
178
+ ```
179
+ X-RateLimit-Limit: 600
180
+ X-RateLimit-Remaining: 597
181
+ X-RateLimit-Reset: 1712534400
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Security Best Practices
187
+
188
+ 1. **Never commit API keys to source control.** Use `.env` files or secret managers.
189
+ 2. **Use the minimum scopes required.** A firewall key only needs `firewall:read`.
190
+ 3. **Restrict by IP when possible.** Server keys should be locked to your server IPs.
191
+ 4. **Rotate keys periodically.** Use the regenerate feature to rotate without downtime.
192
+ 5. **Monitor usage.** Check the "last used" timestamp and known IPs in the dashboard.
193
+ 6. **Revoke unused keys.** Delete keys that are no longer in use.
194
+
195
+ ---
196
+
197
+ ## API Versioning
198
+
199
+ All API endpoints are available at both `/api/` and `/api/v1/`. We recommend using the versioned path for stability:
200
+
201
+ ```bash
202
+ # Recommended
203
+ https://api.securenow.ai/api/v1/blocklist
204
+
205
+ # Also works (unversioned)
206
+ https://api.securenow.ai/api/blocklist
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Related Documentation
212
+
213
+ - [Firewall Guide](./FIREWALL-GUIDE.md) — Automatic IP blocking setup
214
+ - [Environment Variables Reference](./ENVIRONMENT-VARIABLES.md) — All configuration options
215
+ - [All Frameworks Quick Start](./ALL-FRAMEWORKS-QUICKSTART.md) — Framework setup guides