securenow 7.7.0 → 7.7.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/NPM_README.md +5 -2
- package/README.md +9 -4
- package/SKILL-API.md +2 -2
- package/SKILL-CLI.md +7 -6
- package/app-config.js +81 -3
- package/cli/config.js +8 -8
- package/cli/credentials.js +2 -1
- package/package.json +1 -1
package/NPM_README.md
CHANGED
|
@@ -415,8 +415,9 @@ Config files are stored in `~/.securenow/` (global) or `.securenow/` in the proj
|
|
|
415
415
|
| `~/.securenow/config.json` | API URL, default app, output format |
|
|
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
|
+
| `.securenow/credentials.<environment>.json` | Tokenless runtime credentials generated by `credentials runtime --env <environment>` |
|
|
418
419
|
|
|
419
|
-
**Resolution order:** project `.securenow/credentials.json` -> global `~/.securenow/credentials.json`. Legacy CLI token overrides still work for existing automation.
|
|
420
|
+
**Resolution order:** project `.securenow/credentials.json` -> project `.securenow/credentials.<environment>.json` -> global `~/.securenow/credentials.json` -> global `~/.securenow/credentials.<environment>.json`. Legacy CLI token overrides still work for existing automation.
|
|
420
421
|
|
|
421
422
|
### Global Flags
|
|
422
423
|
|
|
@@ -464,6 +465,8 @@ npx securenow credentials runtime --env production
|
|
|
464
465
|
|
|
465
466
|
# Store .securenow/credentials.production.json as a deployment secret file,
|
|
466
467
|
# then materialize it as .securenow/credentials.json in the running app.
|
|
468
|
+
# Since v7.7.1, mounting it as .securenow/credentials.production.json
|
|
469
|
+
# also works when the canonical credentials.json file is absent.
|
|
467
470
|
|
|
468
471
|
# Use --json for machine-readable output
|
|
469
472
|
npx securenow logs --json --level error | jq '.logs'
|
|
@@ -1073,7 +1076,7 @@ npx securenow api-key set snk_live_abc123...
|
|
|
1073
1076
|
npx securenow credentials runtime --env production
|
|
1074
1077
|
```
|
|
1075
1078
|
|
|
1076
|
-
The SDK resolves the firewall key from project `./.securenow/credentials.json`, then global `~/.securenow/credentials.json`. Legacy `SECURENOW_API_KEY` overrides still work for existing deployments.
|
|
1079
|
+
The SDK resolves the firewall key from project `./.securenow/credentials.json`, then project `./.securenow/credentials.<environment>.json`, then global `~/.securenow/credentials.json`, then global `~/.securenow/credentials.<environment>.json`. Legacy `SECURENOW_API_KEY` overrides still work for existing deployments.
|
|
1077
1080
|
|
|
1078
1081
|
On startup, you'll see:
|
|
1079
1082
|
|
package/README.md
CHANGED
|
@@ -152,11 +152,15 @@ npx securenow credentials runtime --env production
|
|
|
152
152
|
|
|
153
153
|
It writes `.securenow/credentials.production.json`, with the same `app`, `apiKey`, `config`, and `_securenow.explanations` shape, but without the CLI OAuth `token`, `email`, or `expiresAt`. Store that JSON in your deployment secret manager and materialize it as `.securenow/credentials.json` at runtime.
|
|
154
154
|
|
|
155
|
+
Starting in v7.7.1, the SDK also accepts the generated filename directly. If `.securenow/credentials.json` is missing, it checks `.securenow/credentials.<environment>.json`, so a production app with `NODE_ENV=production` can mount `.securenow/credentials.production.json` directly.
|
|
156
|
+
|
|
155
157
|
Resolution order:
|
|
156
158
|
|
|
157
159
|
1. Project-local `.securenow/credentials.json`
|
|
158
|
-
2.
|
|
159
|
-
3.
|
|
160
|
+
2. Project-local `.securenow/credentials.<environment>.json`
|
|
161
|
+
3. Global `~/.securenow/credentials.json`
|
|
162
|
+
4. Global `~/.securenow/credentials.<environment>.json`
|
|
163
|
+
5. `package.json#name` (label only)
|
|
160
164
|
|
|
161
165
|
Legacy environment variables are fallback-only for existing installs. New local, CI, Docker, and production setups should use the credentials file.
|
|
162
166
|
|
|
@@ -395,11 +399,12 @@ After install, the `securenow` CLI is available via `npx securenow` or globally
|
|
|
395
399
|
| File | Purpose |
|
|
396
400
|
|---|---|
|
|
397
401
|
| `./.securenow/credentials.json` | Project-local or production runtime credentials |
|
|
398
|
-
| `./.securenow/credentials
|
|
402
|
+
| `./.securenow/credentials.<environment>.json` | Tokenless runtime file generated by `securenow credentials runtime --env <environment>` |
|
|
399
403
|
| `~/.securenow/credentials.json` | Global (with `login --global`) |
|
|
404
|
+
| `~/.securenow/credentials.<environment>.json` | Global environment-specific runtime credentials |
|
|
400
405
|
| `~/.securenow/config.json` | API URL, default app, preferences |
|
|
401
406
|
|
|
402
|
-
Resolution order: project `.securenow
|
|
407
|
+
Resolution order: project `.securenow/credentials.json` -> project `.securenow/credentials.<environment>.json` -> global `~/.securenow/credentials.json` -> global `~/.securenow/credentials.<environment>.json` -> package name fallback. Legacy env vars are fallback-only for older installs.
|
|
403
408
|
|
|
404
409
|
Override the dashboard API with `securenow config set apiUrl <url>`.
|
|
405
410
|
|
package/SKILL-API.md
CHANGED
|
@@ -273,7 +273,7 @@ Instruments document load, fetch, XMLHttpRequest, and user interactions with bro
|
|
|
273
273
|
|
|
274
274
|
## Firewall — Multi-Layer IP Blocking
|
|
275
275
|
|
|
276
|
-
The firewall auto-activates once an API key is resolvable and the app firewall toggle is on. Since **v7.5.1**, `npx securenow login` enables the selected app firewall by default and writes the scoped key to `.securenow/credentials.json`; `securenow api-key set` can still write/rotate the key later. Production should use the tokenless file generated by `securenow credentials runtime --env production`. Resolution order: project `./.securenow/credentials.json` -> global `~/.securenow/credentials.json`; legacy env vars are fallback-only for existing deployments.
|
|
276
|
+
The firewall auto-activates once an API key is resolvable and the app firewall toggle is on. Since **v7.5.1**, `npx securenow login` enables the selected app firewall by default and writes the scoped key to `.securenow/credentials.json`; `securenow api-key set` can still write/rotate the key later. Production should use the tokenless file generated by `securenow credentials runtime --env production`. Resolution order: project `./.securenow/credentials.json` -> project `./.securenow/credentials.<environment>.json` -> global `~/.securenow/credentials.json` -> global `~/.securenow/credentials.<environment>.json`; legacy env vars are fallback-only for existing deployments.
|
|
277
277
|
|
|
278
278
|
```
|
|
279
279
|
Layer 4: Cloud/Edge WAF → blocked at CDN (Cloudflare, AWS WAF, GCP Cloud Armor)
|
|
@@ -457,7 +457,7 @@ securenow redact @request.json --fields internal_id,sessionHash
|
|
|
457
457
|
|
|
458
458
|
## Credentials Configuration
|
|
459
459
|
|
|
460
|
-
Local development and production use `.securenow/credentials.json`. Every setting below lives under `app` or `config`; `npx securenow credentials runtime --env production` creates a tokenless production file with the same structure. Environment variables are legacy fallbacks only.
|
|
460
|
+
Local development and production use `.securenow/credentials.json`. Every setting below lives under `app` or `config`; `npx securenow credentials runtime --env production` creates a tokenless production file with the same structure. Since v7.7.1, the SDK also accepts `.securenow/credentials.<environment>.json` when the canonical `credentials.json` file is absent, so `.securenow/credentials.production.json` can be mounted directly. Environment variables are legacy fallbacks only.
|
|
461
461
|
|
|
462
462
|
### App Identity
|
|
463
463
|
|
package/SKILL-CLI.md
CHANGED
|
@@ -39,11 +39,11 @@ securenow whoami # verify session (shows email, app, auth source)
|
|
|
39
39
|
|
|
40
40
|
**Default-on security (v7.5.1+):** after picking or creating the app, `securenow login` turns on that app's firewall toggle, mints an API key with `firewall:read + blocklist:read + allowlist:read` scopes, and writes it into `.securenow/credentials.json`. Traces, logs, POST body capture, multipart metadata capture, and the firewall are enabled by default. No `SECURENOW_API_KEY` env var is needed. To add or rotate a key later without re-running login, use `securenow api-key set snk_live_...` (see [API Key Management](#api-key-management) below).
|
|
41
41
|
|
|
42
|
-
Credentials resolve in order: project `.securenow/credentials.json` -> global `~/.securenow/credentials.json`. Legacy env vars are fallback-only for existing deployments.
|
|
42
|
+
Credentials resolve in order: project `.securenow/credentials.json` -> project `.securenow/credentials.<environment>.json` -> global `~/.securenow/credentials.json` -> global `~/.securenow/credentials.<environment>.json`. Legacy env vars are fallback-only for existing deployments.
|
|
43
43
|
|
|
44
44
|
The **firewall API key** should live in the same credentials file as `apiKey`. Legacy `SECURENOW_API_KEY` overrides are honored only when they start with `snk_live_`.
|
|
45
45
|
|
|
46
|
-
For CI / Docker / production, use `securenow credentials runtime --env production` to generate a tokenless runtime file, then mount/copy it as `.securenow/credentials.json`.
|
|
46
|
+
For CI / Docker / production, use `securenow credentials runtime --env production` to generate a tokenless runtime file, then mount/copy it as `.securenow/credentials.json`. Since v7.7.1, mounting the generated `.securenow/credentials.production.json` filename directly also works when `credentials.json` is absent.
|
|
47
47
|
|
|
48
48
|
**Environment model:** use one SecureNow app key for local, preview, staging, and production. The credentials field `config.runtime.deploymentEnvironment` separates traces/logs/firewall/forensics by environment. CLI security commands default to `production`; pass `--env local`, `--env staging`, or `--env all` only when that scope is intentional.
|
|
49
49
|
|
|
@@ -81,10 +81,11 @@ Config lives in `~/.securenow/` (global) and optionally `.securenow/` (per-proje
|
|
|
81
81
|
| `~/.securenow/config.json` | `apiUrl`, `appUrl`, `defaultApp`, `output` |
|
|
82
82
|
| `~/.securenow/credentials.json` | `token`, `email`, `expiresAt`, `apiKey`, `app`, `config` (global, use `login --global`) |
|
|
83
83
|
| `.securenow/credentials.json` | `token`, `email`, `expiresAt`, `apiKey`, `app`, `config`, `_securenow.explanations` (project-local default) |
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
| `.securenow/credentials.<environment>.json` | Tokenless runtime credentials generated by `securenow credentials runtime --env <environment>` |
|
|
85
|
+
|
|
86
|
+
**Credential resolution order:** `.securenow/credentials.json` (project) -> `.securenow/credentials.<environment>.json` (project) -> `~/.securenow/credentials.json` (global) -> `~/.securenow/credentials.<environment>.json` (global). Legacy env vars are fallback-only for existing deployments.
|
|
87
|
+
|
|
88
|
+
**Firewall API key resolution (v7.5.1+):** project `.securenow/credentials.json` -> project `.securenow/credentials.<environment>.json` -> global `~/.securenow/credentials.json` -> global `~/.securenow/credentials.<environment>.json`. Use `securenow login` for default setup or `securenow api-key set` to rotate a key without touching env vars.
|
|
88
89
|
|
|
89
90
|
```bash
|
|
90
91
|
securenow config set apiUrl https://api.securenow.ai
|
package/app-config.js
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* Shared SecureNow configuration resolver.
|
|
5
5
|
*
|
|
6
6
|
* Local development and production are driven by ./.securenow/credentials.json.
|
|
7
|
+
* Environment-specific runtime files such as
|
|
8
|
+
* ./.securenow/credentials.production.json are also accepted when the
|
|
9
|
+
* canonical file is not present.
|
|
7
10
|
* Legacy environment variables are only fallback inputs for existing installs;
|
|
8
11
|
* every SDK setting has a file-backed equivalent so customers do not need .env
|
|
9
12
|
* files.
|
|
@@ -157,7 +160,9 @@ function clone(value) {
|
|
|
157
160
|
|
|
158
161
|
function readJsonSafe(filepath) {
|
|
159
162
|
try {
|
|
160
|
-
|
|
163
|
+
let content = fs.readFileSync(filepath, 'utf8');
|
|
164
|
+
if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1);
|
|
165
|
+
return JSON.parse(content);
|
|
161
166
|
} catch {
|
|
162
167
|
return null;
|
|
163
168
|
}
|
|
@@ -191,6 +196,30 @@ function findUpFile(startDir, relativePath) {
|
|
|
191
196
|
}
|
|
192
197
|
}
|
|
193
198
|
|
|
199
|
+
function findUpFirstFile(startDir, relativePaths) {
|
|
200
|
+
if (!startDir) return null;
|
|
201
|
+
const candidates = Array.isArray(relativePaths) ? relativePaths.filter(Boolean) : [relativePaths].filter(Boolean);
|
|
202
|
+
if (!candidates.length) return null;
|
|
203
|
+
|
|
204
|
+
let dir;
|
|
205
|
+
try {
|
|
206
|
+
dir = path.resolve(startDir);
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
while (true) {
|
|
212
|
+
for (const relativePath of candidates) {
|
|
213
|
+
const found = fileIfReadable(path.join(dir, relativePath));
|
|
214
|
+
if (found) return found;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const parent = path.dirname(dir);
|
|
218
|
+
if (!parent || parent === dir) return null;
|
|
219
|
+
dir = parent;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
194
223
|
function uniq(values) {
|
|
195
224
|
const seen = new Set();
|
|
196
225
|
const out = [];
|
|
@@ -202,6 +231,35 @@ function uniq(values) {
|
|
|
202
231
|
return out;
|
|
203
232
|
}
|
|
204
233
|
|
|
234
|
+
function credentialEnvironmentNames() {
|
|
235
|
+
const names = [];
|
|
236
|
+
const rawValues = [
|
|
237
|
+
rawEnv('SECURENOW_ENVIRONMENT'),
|
|
238
|
+
rawEnv('SECURENOW_DEPLOYMENT_ENVIRONMENT'),
|
|
239
|
+
rawEnv('NODE_ENV'),
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
for (const rawValue of rawValues) {
|
|
243
|
+
const value = pick(rawValue);
|
|
244
|
+
if (value == null) continue;
|
|
245
|
+
const text = String(value).trim().toLowerCase();
|
|
246
|
+
if (/^[a-z0-9_.-]{1,64}$/.test(text)) names.push(text);
|
|
247
|
+
names.push(normalizeDeploymentEnvironment(text));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
names.push('production');
|
|
251
|
+
return uniq(names);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function credentialRelativePaths() {
|
|
255
|
+
return uniq([
|
|
256
|
+
path.join('.securenow', 'credentials.json'),
|
|
257
|
+
...credentialEnvironmentNames().map((envName) =>
|
|
258
|
+
path.join('.securenow', `credentials.${envName}.json`)
|
|
259
|
+
),
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
262
|
+
|
|
205
263
|
function resolveLocalCredentialsFile() {
|
|
206
264
|
const starts = [];
|
|
207
265
|
try {
|
|
@@ -212,8 +270,25 @@ function resolveLocalCredentialsFile() {
|
|
|
212
270
|
if (process.argv && process.argv[1]) starts.push(path.dirname(process.argv[1]));
|
|
213
271
|
if (require.main && require.main.filename) starts.push(path.dirname(require.main.filename));
|
|
214
272
|
|
|
273
|
+
const candidates = credentialRelativePaths();
|
|
215
274
|
for (const start of uniq(starts)) {
|
|
216
|
-
const found =
|
|
275
|
+
const found = findUpFirstFile(start, candidates);
|
|
276
|
+
if (found) return found;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function resolveGlobalCredentialsFile() {
|
|
283
|
+
let home;
|
|
284
|
+
try {
|
|
285
|
+
home = os.homedir();
|
|
286
|
+
} catch {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
for (const relativePath of credentialRelativePaths()) {
|
|
291
|
+
const found = fileIfReadable(path.join(home, relativePath));
|
|
217
292
|
if (found) return found;
|
|
218
293
|
}
|
|
219
294
|
|
|
@@ -248,7 +323,7 @@ function loadLocalCredentials() {
|
|
|
248
323
|
|
|
249
324
|
function loadGlobalCredentials() {
|
|
250
325
|
try {
|
|
251
|
-
return withCredentialDefaults(readJsonSafe(
|
|
326
|
+
return withCredentialDefaults(readJsonSafe(resolveGlobalCredentialsFile()));
|
|
252
327
|
} catch {
|
|
253
328
|
return null;
|
|
254
329
|
}
|
|
@@ -668,6 +743,9 @@ module.exports = {
|
|
|
668
743
|
listEnv,
|
|
669
744
|
parseHeaders,
|
|
670
745
|
headersToString,
|
|
746
|
+
credentialRelativePaths,
|
|
747
|
+
resolveLocalCredentialsFile,
|
|
748
|
+
resolveGlobalCredentialsFile,
|
|
671
749
|
loadCredentials,
|
|
672
750
|
loadLocalCredentials,
|
|
673
751
|
loadGlobalCredentials,
|
package/cli/config.js
CHANGED
|
@@ -28,7 +28,9 @@ function ensureDir(dir) {
|
|
|
28
28
|
|
|
29
29
|
function loadJSON(filepath) {
|
|
30
30
|
try {
|
|
31
|
-
|
|
31
|
+
let content = fs.readFileSync(filepath, 'utf8');
|
|
32
|
+
if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1);
|
|
33
|
+
return JSON.parse(content);
|
|
32
34
|
} catch {
|
|
33
35
|
return {};
|
|
34
36
|
}
|
|
@@ -50,17 +52,16 @@ function credentialsFileForLocal(local) {
|
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
function hasLocalCredentials() {
|
|
53
|
-
return
|
|
55
|
+
return !!appConfig.resolveLocalCredentialsFile();
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
function resolveCredentialsFile() {
|
|
57
|
-
|
|
58
|
-
return CREDENTIALS_FILE;
|
|
59
|
+
return appConfig.resolveLocalCredentialsFile() || appConfig.resolveGlobalCredentialsFile() || CREDENTIALS_FILE;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
function getAuthSource() {
|
|
62
63
|
if (process.env.SECURENOW_TOKEN) return 'env (SECURENOW_TOKEN)';
|
|
63
|
-
if (
|
|
64
|
+
if (appConfig.resolveLocalCredentialsFile()) return 'project (.securenow/)';
|
|
64
65
|
return 'global (~/.securenow/)';
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -91,9 +92,8 @@ function setConfigValue(key, value) {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
function loadCredentials() {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
: loadJSON(CREDENTIALS_FILE);
|
|
95
|
+
const credentialsFile = resolveCredentialsFile();
|
|
96
|
+
const credentials = loadJSON(credentialsFile);
|
|
97
97
|
return appConfig.withCredentialDefaults(credentials) || {};
|
|
98
98
|
}
|
|
99
99
|
|
package/cli/credentials.js
CHANGED
|
@@ -40,7 +40,7 @@ function buildRuntimeCredentials(options = {}) {
|
|
|
40
40
|
},
|
|
41
41
|
_securenow: {
|
|
42
42
|
...(creds._securenow || {}),
|
|
43
|
-
note: 'Runtime SecureNow credentials and SDK defaults. Mount or copy this JSON as .securenow/credentials.json in production. Do not commit it.',
|
|
43
|
+
note: 'Runtime SecureNow credentials and SDK defaults. Mount or copy this JSON as .securenow/credentials.json or .securenow/credentials.<environment>.json in production. Do not commit it.',
|
|
44
44
|
runtimeOnly: 'This file intentionally omits CLI OAuth fields: token, email, and expiresAt.',
|
|
45
45
|
production: 'Production can use this same file shape instead of environment variables.',
|
|
46
46
|
},
|
|
@@ -83,6 +83,7 @@ async function runtime(_args, flags) {
|
|
|
83
83
|
|
|
84
84
|
ui.success(`Wrote runtime credentials to ${output}`);
|
|
85
85
|
ui.info('Deploy this JSON as .securenow/credentials.json on the server/container.');
|
|
86
|
+
ui.info('SDK v7.7.1+ can also read this generated filename directly when credentials.json is absent.');
|
|
86
87
|
ui.info(`Environment: ${envName}`);
|
|
87
88
|
ui.info(`App: ${creds.app?.name || '(unnamed)'} ${creds.app?.key ? `(${creds.app.key})` : ''}`);
|
|
88
89
|
ui.info(`Firewall key: ${creds.apiKey ? maskSecret(creds.apiKey) : '(missing)'}`);
|
package/package.json
CHANGED