securenow 7.6.7 → 7.6.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/NPM_README.md +13 -13
- package/README.md +21 -37
- package/app-config.js +5 -3
- package/cli/config.js +4 -3
- package/cli/diagnostics.js +54 -15
- package/cli/run.js +40 -11
- package/firewall-only.js +1 -1
- package/mcp/catalog.js +1 -1
- package/nextjs-webpack-config.js +3 -15
- package/nextjs.js +21 -23
- package/nuxt-server-plugin.mjs +20 -10
- package/package.json +23 -33
- package/register.js +1 -1
- package/tracing.js +17 -7
- package/web-vite.mjs +23 -13
- package/CONSUMING-APPS-GUIDE.md +0 -463
- package/docs/ALL-FRAMEWORKS-QUICKSTART.md +0 -1388
- package/docs/API-KEYS-GUIDE.md +0 -278
- package/docs/ARCHITECTURE.md +0 -408
- package/docs/AUTO-BODY-CAPTURE.md +0 -412
- package/docs/AUTO-SETUP-SUMMARY.md +0 -331
- package/docs/AUTO-SETUP.md +0 -419
- package/docs/AUTOMATIC-IP-CAPTURE.md +0 -359
- package/docs/BODY-CAPTURE-FIX.md +0 -261
- package/docs/BODY-CAPTURE-QUICKSTART.md +0 -147
- package/docs/CHANGELOG-NEXTJS.md +0 -235
- package/docs/COMPLETION-REPORT.md +0 -408
- package/docs/CUSTOMER-GUIDE.md +0 -364
- package/docs/EASIEST-SETUP.md +0 -342
- package/docs/ENVIRONMENT-VARIABLES.md +0 -166
- package/docs/ENVIRONMENTS.md +0 -60
- package/docs/EXPRESS-BODY-CAPTURE.md +0 -1028
- package/docs/EXPRESS-SETUP-GUIDE.md +0 -722
- package/docs/FINAL-SOLUTION.md +0 -335
- package/docs/FIREWALL-GUIDE.md +0 -440
- package/docs/IMPLEMENTATION-SUMMARY.md +0 -410
- package/docs/INDEX.md +0 -222
- package/docs/LOGGING-GUIDE.md +0 -704
- package/docs/LOGGING-QUICKSTART.md +0 -221
- package/docs/MCP-GUIDE.md +0 -58
- package/docs/NEXTJS-BODY-CAPTURE-COMPARISON.md +0 -323
- package/docs/NEXTJS-BODY-CAPTURE.md +0 -368
- package/docs/NEXTJS-GUIDE.md +0 -392
- package/docs/NEXTJS-QUICKSTART.md +0 -83
- package/docs/NEXTJS-SETUP-COMPLETE.md +0 -795
- package/docs/NEXTJS-WEBPACK-WARNINGS.md +0 -267
- package/docs/NEXTJS-WRAPPER-APPROACH.md +0 -414
- package/docs/NUXT-GUIDE.md +0 -173
- package/docs/QUICKSTART-BODY-CAPTURE.md +0 -293
- package/docs/REDACTION-EXAMPLES.md +0 -484
- package/docs/REQUEST-BODY-CAPTURE.md +0 -587
- package/docs/SOLUTION-SUMMARY.md +0 -312
- package/docs/VERCEL-OTEL-MIGRATION.md +0 -255
- package/examples/README.md +0 -265
- package/examples/express-with-logging.js +0 -137
- package/examples/instrumentation-with-auto-capture.ts +0 -41
- package/examples/next.config.js +0 -37
- package/examples/nextjs-api-route-with-body-capture.ts +0 -54
- package/examples/nextjs-env-example.txt +0 -32
- package/examples/nextjs-instrumentation.js +0 -36
- package/examples/nextjs-instrumentation.ts +0 -36
- package/examples/nextjs-middleware.js +0 -37
- package/examples/nextjs-middleware.ts +0 -37
- package/examples/nextjs-with-logging-example.md +0 -301
- package/examples/nextjs-with-options.ts +0 -36
- package/examples/test-nextjs-setup.js +0 -70
- package/postinstall.js +0 -296
package/NPM_README.md
CHANGED
|
@@ -59,7 +59,7 @@ yarn add securenow
|
|
|
59
59
|
|
|
60
60
|
### 1. Automatic Setup (Recommended)
|
|
61
61
|
|
|
62
|
-
Run login
|
|
62
|
+
Run login - it is a browser flow that picks or creates an app and connects the firewall automatically:
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
65
|
npx securenow login
|
|
@@ -138,7 +138,7 @@ You'll see confirmation in the console:
|
|
|
138
138
|
|
|
139
139
|
The `securenow` CLI gives you full access to the SecureNow platform from the terminal -- no browser required for day-to-day workflows. Zero additional dependencies.
|
|
140
140
|
|
|
141
|
-
**Full CLI/SDK parity (v6.1.0+):** every SDK export has a matching CLI command. `redactSensitiveData`
|
|
141
|
+
**Full CLI/SDK parity (v6.1.0+):** every SDK export has a matching CLI command. `redactSensitiveData` -> `securenow redact`, `createMatcher` -> `securenow cidr match`, `getLogger().emit()` -> `securenow log send`, `SECURENOW_TEST_SPAN` -> `securenow test-span`, `node -r securenow/firewall-only` -> `securenow run --firewall-only`. False-positive triage (`fp create`, `fp ai-fill`, `fp mark`) works from the terminal without the web dashboard.
|
|
142
142
|
|
|
143
143
|
### Getting Started
|
|
144
144
|
|
|
@@ -326,7 +326,7 @@ npx securenow instances test <id>
|
|
|
326
326
|
|
|
327
327
|
### False-Positive Management
|
|
328
328
|
|
|
329
|
-
Full FP triage from the terminal
|
|
329
|
+
Full FP triage from the terminal - no dashboard required.
|
|
330
330
|
|
|
331
331
|
```bash
|
|
332
332
|
# Browse & inspect rules
|
|
@@ -380,7 +380,7 @@ SDK helpers surfaced as CLI commands so agents (and humans) can validate behavio
|
|
|
380
380
|
npx securenow redact '{"user":"alice","password":"s3cret","card":"4242"}'
|
|
381
381
|
npx securenow redact @request.json --fields internal_id,sessionHash
|
|
382
382
|
|
|
383
|
-
# CIDR
|
|
383
|
+
# CIDR - match an IP against a list, or parse a range
|
|
384
384
|
npx securenow cidr match 10.0.0.5 10.0.0.0/8,192.168.1.0/24 # exit 0 = hit, 2 = miss
|
|
385
385
|
npx securenow cidr parse 10.0.0.0/24 # network, broadcast, mask, size
|
|
386
386
|
|
|
@@ -416,7 +416,7 @@ Config files are stored in `~/.securenow/` (global) or `.securenow/` in the proj
|
|
|
416
416
|
| `~/.securenow/credentials.json` | Auth token, app, API key, config - global (use `login --global`) |
|
|
417
417
|
| `.securenow/credentials.json` | Auth token, app, API key, config, explanations - project-local default |
|
|
418
418
|
|
|
419
|
-
**Resolution order:** project `.securenow/credentials.json`
|
|
419
|
+
**Resolution order:** project `.securenow/credentials.json` -> global `~/.securenow/credentials.json`. Legacy CLI token overrides still work for existing automation.
|
|
420
420
|
|
|
421
421
|
### Global Flags
|
|
422
422
|
|
|
@@ -432,7 +432,7 @@ Every command supports these flags:
|
|
|
432
432
|
|
|
433
433
|
| Variable | Description |
|
|
434
434
|
|----------|-------------|
|
|
435
|
-
| `SECURENOW_TOKEN` | JWT token
|
|
435
|
+
| `SECURENOW_TOKEN` | JWT token - overrides all file-based credentials |
|
|
436
436
|
| `SECURENOW_API_URL` | Override the API base URL |
|
|
437
437
|
| `SECURENOW_DEBUG` | Show stack traces on errors |
|
|
438
438
|
| `NO_COLOR` | Disable colored output |
|
|
@@ -442,11 +442,11 @@ Every command supports these flags:
|
|
|
442
442
|
Project-local credentials are the default, so separate projects can use separate SecureNow apps on the same machine:
|
|
443
443
|
|
|
444
444
|
```bash
|
|
445
|
-
# In project A
|
|
445
|
+
# In project A - log in as user-a@company.com
|
|
446
446
|
cd ~/projects/project-a
|
|
447
447
|
npx securenow login
|
|
448
448
|
|
|
449
|
-
# In project B
|
|
449
|
+
# In project B - log in as user-b@company.com
|
|
450
450
|
cd ~/projects/project-b
|
|
451
451
|
npx securenow login
|
|
452
452
|
|
|
@@ -935,7 +935,7 @@ app.listen(3000, () => console.log('Feathers running on port 3000'));
|
|
|
935
935
|
|
|
936
936
|
### Next.js
|
|
937
937
|
|
|
938
|
-
|
|
938
|
+
Use `npx securenow init` for the current Next.js integration; it creates or prints the exact merge instructions for `instrumentation.ts` and `next.config.*`.
|
|
939
939
|
|
|
940
940
|
#### Option A: `securenow init` (Recommended)
|
|
941
941
|
|
|
@@ -1101,7 +1101,7 @@ If you only need IP blocking without OpenTelemetry tracing overhead, use the sta
|
|
|
1101
1101
|
# Manual preload flag
|
|
1102
1102
|
node -r securenow/firewall-only app.js
|
|
1103
1103
|
|
|
1104
|
-
# Or via the CLI (v6.1.0+)
|
|
1104
|
+
# Or via the CLI (v6.1.0+) - same effect, clearer intent
|
|
1105
1105
|
securenow run --firewall-only app.js
|
|
1106
1106
|
```
|
|
1107
1107
|
|
|
@@ -1153,7 +1153,7 @@ npx securenow firewall status
|
|
|
1153
1153
|
|
|
1154
1154
|
Or create one from the dashboard with the `firewall:read` scope.
|
|
1155
1155
|
|
|
1156
|
-
|
|
1156
|
+
Use `npx securenow firewall status`, `npx securenow firewall apps`, and `npx securenow help firewall` for the current firewall reference.
|
|
1157
1157
|
|
|
1158
1158
|
---
|
|
1159
1159
|
|
|
@@ -1161,7 +1161,7 @@ See the [Firewall Guide](./docs/FIREWALL-GUIDE.md) for the full reference.
|
|
|
1161
1161
|
|
|
1162
1162
|
Local development and production use `.securenow/credentials.json`. Run `npx securenow login` and `npx securenow init`; for production, run `npx securenow credentials runtime --env production` and mount/copy the generated JSON as `.securenow/credentials.json`.
|
|
1163
1163
|
|
|
1164
|
-
|
|
1164
|
+
Use `.securenow/credentials.json` as the source of truth. Run `npx securenow env --json` to inspect resolved settings without exposing secrets.
|
|
1165
1165
|
|
|
1166
1166
|
### Credentials Fields
|
|
1167
1167
|
|
|
@@ -1247,7 +1247,7 @@ Legacy env fallback aliases are listed below for existing installs only.
|
|
|
1247
1247
|
| `SECURENOW_FIREWALL_CLOUD_DRY_RUN` | Log cloud pushes without applying changes. | `0` |
|
|
1248
1248
|
| `SECURENOW_TRUSTED_PROXIES` | Comma-separated trusted proxy IPs. | - |
|
|
1249
1249
|
|
|
1250
|
-
|
|
1250
|
+
Use `npx securenow help firewall` for complete details on all layers.
|
|
1251
1251
|
|
|
1252
1252
|
#### Debugging
|
|
1253
1253
|
|
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# SecureNow
|
|
2
2
|
|
|
3
|
-
Zero-config OpenTelemetry for Node.js, Next.js, and Nuxt
|
|
3
|
+
Zero-config OpenTelemetry for Node.js, Next.js, and Nuxt: traces, logs, body capture, and IP firewall in one install. **No env vars. No copy-pasting keys.**
|
|
4
4
|
|
|
5
5
|
**Official npm package:** [securenow](http://securenow.ai/)
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## 30-second setup
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
# 1. Install
|
|
@@ -17,7 +17,7 @@ npm install securenow
|
|
|
17
17
|
# a scoped firewall API key in the same file - no env vars.
|
|
18
18
|
npx securenow login
|
|
19
19
|
|
|
20
|
-
# 3. Start your app
|
|
20
|
+
# 3. Start your app - one flag is all it takes
|
|
21
21
|
node -r securenow/register src/index.js
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -108,7 +108,7 @@ const nextConfig = {
|
|
|
108
108
|
export default nextConfig;
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
If a custom config or existing instrumentation file needs human judgment, `init` prints a Codex/Claude-ready prompt with the exact edits to merge.
|
|
111
|
+
If a custom config or existing instrumentation file needs human judgment, `init` prints a Codex/Claude-ready prompt with the exact edits to merge.
|
|
112
112
|
|
|
113
113
|
### Nuxt 3
|
|
114
114
|
|
|
@@ -119,18 +119,18 @@ export default defineNuxtConfig({
|
|
|
119
119
|
});
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
Run `npx securenow init` after installing so the credentials file and secure defaults are created before the app starts.
|
|
123
123
|
|
|
124
124
|
---
|
|
125
125
|
|
|
126
126
|
## What's captured automatically
|
|
127
127
|
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
128
|
+
- HTTP spans (Express, Fastify, NestJS, Koa, Hapi, Next.js, Nuxt, raw `http`)
|
|
129
|
+
- Database spans (Postgres, MySQL, MongoDB, Redis)
|
|
130
|
+
- `console.log/info/warn/error/debug` forwarded as OTLP logs with trace correlation
|
|
131
|
+
- Request body capture (JSON, GraphQL, form-encoded) with auto-redaction of `password`, `token`, `api_key`, `authorization`, `cookie`, etc.
|
|
132
|
+
- Multipart upload metadata (field names, file names, sizes, content-types; never file content)
|
|
133
|
+
- Firewall protection from the selected app's SecureNow blocklist and IPDB sync - activates as soon as you've logged in
|
|
134
134
|
|
|
135
135
|
All of these are **on by default**. Tune them in `.securenow/credentials.json` under the `config` block.
|
|
136
136
|
|
|
@@ -235,7 +235,7 @@ Legacy env fallback aliases:
|
|
|
235
235
|
|
|
236
236
|
| Variable | Default | Purpose |
|
|
237
237
|
|---|---|---|
|
|
238
|
-
| `SECURENOW_APPID` | from credentials file | App routing key (UUID)
|
|
238
|
+
| `SECURENOW_APPID` | from credentials file | App routing key (UUID), sent as OTel `service.name` |
|
|
239
239
|
| `SECURENOW_INSTANCE` | `https://freetrial.securenow.ai:4318` | OTLP collector endpoint |
|
|
240
240
|
| `SECURENOW_API_KEY` | from credentials file | Scoped firewall API key (`snk_live_...`) |
|
|
241
241
|
| `SECURENOW_LOGGING_ENABLED` | `1` (on) | Forward `console.*` as OTLP logs. Set to `0` to disable. |
|
|
@@ -243,26 +243,26 @@ Legacy env fallback aliases:
|
|
|
243
243
|
| `SECURENOW_CAPTURE_MULTIPART` | `1` (on) | Capture multipart metadata (not content). |
|
|
244
244
|
| `SECURENOW_MAX_BODY_SIZE` | `10240` | Max bytes captured per body. |
|
|
245
245
|
| `SECURENOW_SENSITIVE_FIELDS` | `password,token,authorization,...` | Extra fields to redact (comma-separated). |
|
|
246
|
-
| `SECURENOW_DISABLE_INSTRUMENTATIONS` |
|
|
246
|
+
| `SECURENOW_DISABLE_INSTRUMENTATIONS` | - | Comma-separated OTel instrumentations to disable. |
|
|
247
247
|
| `SECURENOW_NO_UUID` | `0` | Don't append a UUID to `service.instance.id`. |
|
|
248
248
|
| `SECURENOW_STRICT` | `0` | Exit with code 1 if `SECURENOW_APPID` is missing in a PM2 cluster. |
|
|
249
|
-
| `OTEL_EXPORTER_OTLP_HEADERS` |
|
|
250
|
-
| `OTEL_LOG_LEVEL` |
|
|
249
|
+
| `OTEL_EXPORTER_OTLP_HEADERS` | - | Raw OTLP headers (e.g. `x-api-key=...`). |
|
|
250
|
+
| `OTEL_LOG_LEVEL` | - | `debug`/`info`/`warn`/`error`. |
|
|
251
251
|
|
|
252
|
-
|
|
252
|
+
New installs should use `.securenow/credentials.json`; environment variables remain legacy fallbacks for existing deployments.
|
|
253
253
|
|
|
254
254
|
---
|
|
255
255
|
|
|
256
256
|
## Supported frameworks
|
|
257
257
|
|
|
258
258
|
### Web
|
|
259
|
-
Next.js (App & Pages Router)
|
|
259
|
+
Next.js (App & Pages Router), Nuxt 3, Express, Fastify, NestJS, Koa, Hapi
|
|
260
260
|
|
|
261
261
|
### Databases
|
|
262
|
-
PostgreSQL
|
|
262
|
+
PostgreSQL, MySQL / MySQL2, MongoDB, Redis
|
|
263
263
|
|
|
264
264
|
### Other
|
|
265
|
-
HTTP/HTTPS
|
|
265
|
+
HTTP/HTTPS, GraphQL, gRPC, and many more via [@opentelemetry/auto-instrumentations-node](https://www.npmjs.com/package/@opentelemetry/auto-instrumentations-node).
|
|
266
266
|
|
|
267
267
|
> MongoDB instrumentation is opt-in (`SECURENOW_ENABLE_MONGODB_INSTRUMENTATION=1`) because older versions corrupted cursors on `mongodb@6.6+`. Safe again since SDK v6.0.2.
|
|
268
268
|
|
|
@@ -270,23 +270,7 @@ HTTP/HTTPS · GraphQL · gRPC · and many more via [@opentelemetry/auto-instrume
|
|
|
270
270
|
|
|
271
271
|
## Documentation
|
|
272
272
|
|
|
273
|
-
|
|
274
|
-
- [Next.js Quick Start](./docs/NEXTJS-QUICKSTART.md)
|
|
275
|
-
- [Nuxt 3 Guide](./docs/NUXT-GUIDE.md)
|
|
276
|
-
- [All Frameworks](./docs/ALL-FRAMEWORKS-QUICKSTART.md)
|
|
277
|
-
- [Logging Quick Start](./docs/LOGGING-QUICKSTART.md)
|
|
278
|
-
|
|
279
|
-
### Complete Guides
|
|
280
|
-
- [Firewall](./docs/FIREWALL-GUIDE.md)
|
|
281
|
-
- [API Keys](./docs/API-KEYS-GUIDE.md)
|
|
282
|
-
- [MCP](./docs/MCP-GUIDE.md)
|
|
283
|
-
- [Next.js Complete](./docs/NEXTJS-GUIDE.md)
|
|
284
|
-
- [Nuxt 3 Complete](./docs/NUXT-GUIDE.md)
|
|
285
|
-
- [Logging Complete](./docs/LOGGING-GUIDE.md)
|
|
286
|
-
- [📚 All Docs](./docs/INDEX.md)
|
|
287
|
-
|
|
288
|
-
### Examples
|
|
289
|
-
- [Code Examples](./examples/)
|
|
273
|
+
The npm package ships the current setup references: this README, `NPM_README.md`, `SKILL-API.md`, `SKILL-CLI.md`, and the MCP resources exposed by `npx securenow mcp`. Older long-form guides are kept in the repository only so customer installs do not receive stale `.env`-first instructions.
|
|
290
274
|
|
|
291
275
|
---
|
|
292
276
|
|
|
@@ -424,7 +408,7 @@ Override the dashboard API with `securenow config set apiUrl <url>`.
|
|
|
424
408
|
## Support
|
|
425
409
|
|
|
426
410
|
- **Website:** [securenow.ai](http://securenow.ai/)
|
|
427
|
-
- **Docs:**
|
|
411
|
+
- **Docs:** this README, `NPM_README.md`, `SKILL-API.md`, `SKILL-CLI.md`, and `npx securenow help`
|
|
428
412
|
- **Issues:** report bugs and requests on GitHub
|
|
429
413
|
|
|
430
414
|
---
|
package/app-config.js
CHANGED
|
@@ -256,9 +256,11 @@ function loadGlobalCredentials() {
|
|
|
256
256
|
|
|
257
257
|
function loadCredentials() {
|
|
258
258
|
try {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
259
|
+
const localFile = resolveLocalCredentialsFile();
|
|
260
|
+
if (localFile) {
|
|
261
|
+
return withCredentialDefaults(readJsonSafe(localFile));
|
|
262
|
+
}
|
|
263
|
+
return loadGlobalCredentials();
|
|
262
264
|
} catch {
|
|
263
265
|
return loadLocalCredentials() || loadGlobalCredentials() || null;
|
|
264
266
|
}
|
package/cli/config.js
CHANGED
|
@@ -91,9 +91,10 @@ function setConfigValue(key, value) {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
function loadCredentials() {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
const credentials = fs.existsSync(LOCAL_CREDENTIALS_FILE)
|
|
95
|
+
? loadJSON(LOCAL_CREDENTIALS_FILE)
|
|
96
|
+
: loadJSON(CREDENTIALS_FILE);
|
|
97
|
+
return appConfig.withCredentialDefaults(credentials) || {};
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
function saveCredentials(creds, { local = false } = {}) {
|
package/cli/diagnostics.js
CHANGED
|
@@ -21,6 +21,7 @@ function resolvedConfig(options = {}) {
|
|
|
21
21
|
: '(auto-generated)';
|
|
22
22
|
const apiKey = firewall.apiKey || '';
|
|
23
23
|
const firewallEnabled = !!apiKey && firewall.enabled;
|
|
24
|
+
const firewallLocalEnabled = firewall.enabled;
|
|
24
25
|
|
|
25
26
|
return {
|
|
26
27
|
serviceName,
|
|
@@ -31,6 +32,7 @@ function resolvedConfig(options = {}) {
|
|
|
31
32
|
logsEndpoint: endpoints.logsUrl,
|
|
32
33
|
headers: endpoints.headers,
|
|
33
34
|
apiKey,
|
|
35
|
+
firewallLocalEnabled,
|
|
34
36
|
apiUrl: config.getApiUrl(),
|
|
35
37
|
loggingEnabled: appConfig.boolEnv('SECURENOW_LOGGING_ENABLED', true),
|
|
36
38
|
captureBody: appConfig.boolEnv('SECURENOW_CAPTURE_BODY', true),
|
|
@@ -263,21 +265,36 @@ async function logSend(args, flags) {
|
|
|
263
265
|
}
|
|
264
266
|
}
|
|
265
267
|
|
|
266
|
-
|
|
268
|
+
function okHttpStatus(status) {
|
|
269
|
+
return status >= 200 && status < 300;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async function probe({ endpoint, method = 'POST', headers = {}, body = null, timeoutMs = 3000 }) {
|
|
267
273
|
try {
|
|
268
274
|
const res = await httpRequest({
|
|
269
|
-
method
|
|
275
|
+
method,
|
|
270
276
|
endpoint,
|
|
271
|
-
headers
|
|
272
|
-
body
|
|
277
|
+
headers,
|
|
278
|
+
body,
|
|
273
279
|
timeoutMs,
|
|
274
280
|
});
|
|
275
|
-
return {
|
|
281
|
+
return {
|
|
282
|
+
ok: okHttpStatus(res.status),
|
|
283
|
+
status: res.status,
|
|
284
|
+
...(okHttpStatus(res.status) ? {} : { error: `HTTP ${res.status}`, body: res.body ? res.body.slice(0, 500) : '' }),
|
|
285
|
+
};
|
|
276
286
|
} catch (err) {
|
|
277
287
|
return { ok: false, error: err.message };
|
|
278
288
|
}
|
|
279
289
|
}
|
|
280
290
|
|
|
291
|
+
function firewallStatusLabel(cfg) {
|
|
292
|
+
if (cfg.firewallEnabled) return ui.c.green('enabled');
|
|
293
|
+
if (!cfg.apiKey) return ui.c.dim('disabled (missing firewall API key)');
|
|
294
|
+
if (!cfg.firewallLocalEnabled) return ui.c.yellow('disabled (config.firewall.enabled=false)');
|
|
295
|
+
return ui.c.dim('disabled');
|
|
296
|
+
}
|
|
297
|
+
|
|
281
298
|
function env(_args, flags) {
|
|
282
299
|
const cfg = resolvedConfig();
|
|
283
300
|
const resolved = {
|
|
@@ -294,6 +311,7 @@ function env(_args, flags) {
|
|
|
294
311
|
captureBody: cfg.captureBody,
|
|
295
312
|
captureMultipart: cfg.captureMultipart,
|
|
296
313
|
noUuid: appConfig.resolveNoUuid(),
|
|
314
|
+
firewallLocalEnabled: cfg.firewallLocalEnabled,
|
|
297
315
|
firewallLayers: cfg.firewallLayers,
|
|
298
316
|
};
|
|
299
317
|
|
|
@@ -311,7 +329,7 @@ function env(_args, flags) {
|
|
|
311
329
|
['Logging', cfg.loggingEnabled ? ui.c.green('enabled') : ui.c.dim('disabled')],
|
|
312
330
|
['Body capture', cfg.captureBody ? ui.c.green('enabled') : ui.c.dim('disabled')],
|
|
313
331
|
['Multipart capture', cfg.captureMultipart ? ui.c.green('enabled') : ui.c.dim('disabled')],
|
|
314
|
-
['Firewall', cfg
|
|
332
|
+
['Firewall', firewallStatusLabel(cfg)],
|
|
315
333
|
]);
|
|
316
334
|
|
|
317
335
|
ui.heading('Resolved credentials');
|
|
@@ -325,22 +343,43 @@ async function doctor(_args, flags) {
|
|
|
325
343
|
const cfg = resolvedConfig();
|
|
326
344
|
const checks = [];
|
|
327
345
|
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
346
|
+
const jsonHeaders = { 'Content-Type': 'application/json', ...cfg.headers };
|
|
347
|
+
|
|
348
|
+
const spin1 = ui.spinner(`Sending doctor span to ${cfg.tracesEndpoint}`);
|
|
349
|
+
const traces = await probe({
|
|
350
|
+
endpoint: cfg.tracesEndpoint,
|
|
351
|
+
headers: jsonHeaders,
|
|
352
|
+
body: JSON.stringify(buildTracePayload(cfg, {
|
|
353
|
+
name: 'securenow.doctor.probe',
|
|
354
|
+
attributes: { 'test.source': 'securenow-cli-doctor' },
|
|
355
|
+
})),
|
|
356
|
+
});
|
|
357
|
+
if (traces.ok) spin1.stop(`Traces endpoint accepted probe (HTTP ${traces.status})`);
|
|
358
|
+
else spin1.fail(`Traces endpoint rejected probe: ${traces.error}`);
|
|
332
359
|
checks.push({ name: 'traces', ...traces });
|
|
333
360
|
|
|
334
|
-
const spin2 = ui.spinner(`
|
|
335
|
-
const logs = await probe(
|
|
336
|
-
|
|
337
|
-
|
|
361
|
+
const spin2 = ui.spinner(`Sending doctor log to ${cfg.logsEndpoint}`);
|
|
362
|
+
const logs = await probe({
|
|
363
|
+
endpoint: cfg.logsEndpoint,
|
|
364
|
+
headers: jsonHeaders,
|
|
365
|
+
body: JSON.stringify(buildLogPayload(cfg, {
|
|
366
|
+
message: 'SecureNow doctor probe',
|
|
367
|
+
level: 'info',
|
|
368
|
+
attributes: { 'test.source': 'securenow-cli-doctor' },
|
|
369
|
+
})),
|
|
370
|
+
});
|
|
371
|
+
if (logs.ok) spin2.stop(`Logs endpoint accepted probe (HTTP ${logs.status})`);
|
|
372
|
+
else spin2.fail(`Logs endpoint rejected probe: ${logs.error}`);
|
|
338
373
|
checks.push({ name: 'logs', ...logs });
|
|
339
374
|
|
|
340
375
|
const token = config.getToken();
|
|
341
376
|
if (cfg.apiKey || token) {
|
|
342
377
|
const spin3 = ui.spinner(`Probing SecureNow API ${cfg.apiUrl}`);
|
|
343
|
-
const api = await probe(
|
|
378
|
+
const api = await probe({
|
|
379
|
+
method: 'GET',
|
|
380
|
+
endpoint: `${cfg.apiUrl.replace(/\/$/, '')}/health`,
|
|
381
|
+
headers: token ? { Authorization: `Bearer ${token}` } : cfg.apiKey ? { Authorization: `Bearer ${cfg.apiKey}` } : {},
|
|
382
|
+
});
|
|
344
383
|
if (api.ok) spin3.stop(`API reachable (HTTP ${api.status})`);
|
|
345
384
|
else spin3.fail(`API unreachable: ${api.error}`);
|
|
346
385
|
checks.push({ name: 'api', ...api });
|
package/cli/run.js
CHANGED
|
@@ -3,8 +3,24 @@
|
|
|
3
3
|
const { spawn } = require('child_process');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
|
-
const ui = require('./ui');
|
|
7
|
-
|
|
6
|
+
const ui = require('./ui');
|
|
7
|
+
|
|
8
|
+
const NODE_FLAGS_WITH_VALUE = new Set([
|
|
9
|
+
'-r',
|
|
10
|
+
'--require',
|
|
11
|
+
'--import',
|
|
12
|
+
'--loader',
|
|
13
|
+
'--experimental-loader',
|
|
14
|
+
'--env-file',
|
|
15
|
+
'--conditions',
|
|
16
|
+
'-C',
|
|
17
|
+
'--icu-data-dir',
|
|
18
|
+
'--inspect-port',
|
|
19
|
+
'--max-old-space-size',
|
|
20
|
+
'--stack-trace-limit',
|
|
21
|
+
'--title',
|
|
22
|
+
]);
|
|
23
|
+
|
|
8
24
|
function isESM(scriptPath) {
|
|
9
25
|
if (scriptPath.endsWith('.mjs')) return true;
|
|
10
26
|
if (scriptPath.endsWith('.cjs')) return false;
|
|
@@ -93,18 +109,31 @@ function run(rawArgs) {
|
|
|
93
109
|
firewallOnly = true;
|
|
94
110
|
continue;
|
|
95
111
|
}
|
|
96
|
-
if (args[i]
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
if (args[i] === '--') {
|
|
113
|
+
scriptIdx = i + 1;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
if (args[i].startsWith('-')) {
|
|
117
|
+
const flag = args[i];
|
|
118
|
+
nodeFlags.push(flag);
|
|
119
|
+
const flagName = flag.split('=')[0];
|
|
120
|
+
if (NODE_FLAGS_WITH_VALUE.has(flagName) && !flag.includes('=')) {
|
|
121
|
+
if (i + 1 >= args.length) {
|
|
122
|
+
ui.error(`Node flag ${flag} requires a value.`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
nodeFlags.push(args[++i]);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
scriptIdx = i;
|
|
129
|
+
break;
|
|
101
130
|
}
|
|
102
131
|
}
|
|
103
132
|
|
|
104
|
-
if (scriptIdx === -1) {
|
|
105
|
-
ui.error('No script path found. Provide a .js/.mjs/.ts file to run.');
|
|
106
|
-
process.exit(1);
|
|
107
|
-
}
|
|
133
|
+
if (scriptIdx === -1 || scriptIdx >= args.length) {
|
|
134
|
+
ui.error('No script path found. Provide a .js/.mjs/.ts file to run.');
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
108
137
|
|
|
109
138
|
const scriptPath = args[scriptIdx];
|
|
110
139
|
const appArgs = args.slice(scriptIdx + 1);
|
package/firewall-only.js
CHANGED
package/mcp/catalog.js
CHANGED
|
@@ -32,7 +32,7 @@ Runbook:
|
|
|
32
32
|
node -p "require('./node_modules/securenow/package.json').version"
|
|
33
33
|
npx securenow version
|
|
34
34
|
Stop and fix the install if either is below 7.5.1 or npx still resolves an older local package.
|
|
35
|
-
3. Read the installed package surface before editing files: node_modules/securenow/package.json, README/NPM_README, SKILL-API, SKILL-CLI,
|
|
35
|
+
3. Read the installed package surface before editing files: node_modules/securenow/package.json, README/NPM_README, SKILL-API, SKILL-CLI, npx securenow help, and relevant subcommand help for login/init/firewall/doctor/env/test-span/log/mcp.
|
|
36
36
|
4. Mandatory auth gate:
|
|
37
37
|
- Run npx securenow whoami from the project root.
|
|
38
38
|
- If not logged in, run npx securenow login from the project root and wait for the browser flow.
|
package/nextjs-webpack-config.js
CHANGED
|
@@ -16,21 +16,9 @@
|
|
|
16
16
|
* module.exports = { webpack: (config, opts) => getSecureNowWebpackConfig(config, opts) };
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
const EXTERNAL_PACKAGES = [
|
|
20
|
-
'securenow',
|
|
21
|
-
|
|
22
|
-
'@opentelemetry/auto-instrumentations-node',
|
|
23
|
-
'@opentelemetry/instrumentation-http',
|
|
24
|
-
'@opentelemetry/exporter-trace-otlp-http',
|
|
25
|
-
'@opentelemetry/exporter-logs-otlp-http',
|
|
26
|
-
'@opentelemetry/sdk-logs',
|
|
27
|
-
'@opentelemetry/instrumentation',
|
|
28
|
-
'@opentelemetry/resources',
|
|
29
|
-
'@opentelemetry/semantic-conventions',
|
|
30
|
-
'@opentelemetry/api',
|
|
31
|
-
'@opentelemetry/api-logs',
|
|
32
|
-
'@vercel/otel',
|
|
33
|
-
];
|
|
19
|
+
const EXTERNAL_PACKAGES = [
|
|
20
|
+
'securenow',
|
|
21
|
+
];
|
|
34
22
|
|
|
35
23
|
function detectNextMajor() {
|
|
36
24
|
try {
|
package/nextjs.js
CHANGED
|
@@ -5,31 +5,19 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Usage in Next.js app:
|
|
7
7
|
*
|
|
8
|
-
* 1. Add
|
|
8
|
+
* 1. Add securenow to serverExternalPackages in next.config.js:
|
|
9
9
|
*
|
|
10
10
|
* const nextConfig = {
|
|
11
|
-
* serverExternalPackages: [
|
|
12
|
-
* "securenow",
|
|
13
|
-
* "@opentelemetry/sdk-node",
|
|
14
|
-
* "@opentelemetry/auto-instrumentations-node",
|
|
15
|
-
* "@opentelemetry/instrumentation-http",
|
|
16
|
-
* "@opentelemetry/exporter-trace-otlp-http",
|
|
17
|
-
* "@opentelemetry/exporter-logs-otlp-http",
|
|
18
|
-
* "@opentelemetry/sdk-logs",
|
|
19
|
-
* "@opentelemetry/instrumentation",
|
|
20
|
-
* "@opentelemetry/resources",
|
|
21
|
-
* "@opentelemetry/semantic-conventions",
|
|
22
|
-
* "@opentelemetry/api",
|
|
23
|
-
* "@opentelemetry/api-logs",
|
|
24
|
-
* "@vercel/otel",
|
|
25
|
-
* ],
|
|
11
|
+
* serverExternalPackages: ["securenow"],
|
|
26
12
|
* };
|
|
27
13
|
*
|
|
28
14
|
* 2. Create instrumentation.ts (or .js) in your project root:
|
|
29
15
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
16
|
+
* export async function register() {
|
|
17
|
+
* if (process.env.NEXT_RUNTIME !== "nodejs") return;
|
|
18
|
+
* const securenowNext = await import("securenow/nextjs");
|
|
19
|
+
* securenowNext.registerSecureNow({ captureBody: true });
|
|
20
|
+
* await import("securenow/nextjs-auto-capture");
|
|
33
21
|
* }
|
|
34
22
|
*
|
|
35
23
|
* 3. Run `npx securenow login` and `npx securenow init`.
|
|
@@ -39,11 +27,22 @@
|
|
|
39
27
|
|
|
40
28
|
const { randomUUID } = require('crypto');
|
|
41
29
|
const appConfig = require('./app-config');
|
|
30
|
+
const otelResources = require('@opentelemetry/resources');
|
|
42
31
|
|
|
43
32
|
const env = appConfig.env;
|
|
44
33
|
|
|
45
34
|
let isRegistered = false;
|
|
46
35
|
|
|
36
|
+
function createResource(attributes) {
|
|
37
|
+
if (typeof otelResources.resourceFromAttributes === 'function') {
|
|
38
|
+
return otelResources.resourceFromAttributes(attributes);
|
|
39
|
+
}
|
|
40
|
+
if (typeof otelResources.Resource === 'function') {
|
|
41
|
+
return new otelResources.Resource(attributes);
|
|
42
|
+
}
|
|
43
|
+
throw new Error('Unsupported @opentelemetry/resources version');
|
|
44
|
+
}
|
|
45
|
+
|
|
47
46
|
// Default sensitive fields to redact from request bodies
|
|
48
47
|
const DEFAULT_SENSITIVE_FIELDS = [
|
|
49
48
|
'password', 'passwd', 'pwd', 'secret', 'token', 'api_key', 'apikey',
|
|
@@ -430,7 +429,6 @@ function registerSecureNow(options = {}) {
|
|
|
430
429
|
// -------- Self-Hosted (EC2/PM2): Use Vanilla OpenTelemetry SDK --------
|
|
431
430
|
const { NodeSDK } = require('@opentelemetry/sdk-node');
|
|
432
431
|
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
|
|
433
|
-
const { Resource } = require('@opentelemetry/resources');
|
|
434
432
|
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
|
|
435
433
|
|
|
436
434
|
const traceExporter = new OTLPTraceExporter({
|
|
@@ -442,7 +440,7 @@ function registerSecureNow(options = {}) {
|
|
|
442
440
|
serviceName: serviceName,
|
|
443
441
|
traceExporter: traceExporter,
|
|
444
442
|
instrumentations: [httpInstrumentation],
|
|
445
|
-
resource:
|
|
443
|
+
resource: createResource({
|
|
446
444
|
[SemanticResourceAttributes.SERVICE_NAME]: serviceName,
|
|
447
445
|
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: deploymentEnvironment,
|
|
448
446
|
[SemanticResourceAttributes.SERVICE_VERSION]: process.env.npm_package_version || undefined,
|
|
@@ -466,12 +464,12 @@ function registerSecureNow(options = {}) {
|
|
|
466
464
|
});
|
|
467
465
|
|
|
468
466
|
const loggerProvider = new LoggerProvider({
|
|
469
|
-
resource:
|
|
467
|
+
resource: createResource({
|
|
470
468
|
[SemanticResourceAttributes.SERVICE_NAME]: serviceName,
|
|
471
469
|
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: deploymentEnvironment,
|
|
472
470
|
}),
|
|
471
|
+
processors: [new BatchLogRecordProcessor(logExporter)],
|
|
473
472
|
});
|
|
474
|
-
loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));
|
|
475
473
|
|
|
476
474
|
// Patch console to forward logs as OTLP log records
|
|
477
475
|
const logger = loggerProvider.getLogger('console', '1.0.0');
|