@anomira/node-sdk 0.1.6 → 0.1.7
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/README.md +28 -6
- package/dist/cli.cjs +45 -19
- package/dist/index.cjs +58 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -22
- package/dist/index.d.ts +45 -22
- package/dist/index.js +56 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
2
|
import { FastifyPluginAsync } from 'fastify';
|
|
3
3
|
|
|
4
|
-
interface
|
|
5
|
-
/** SDK API key — get this from the
|
|
4
|
+
interface AnomiraConfig {
|
|
5
|
+
/** SDK API key — get this from the Anomira dashboard */
|
|
6
6
|
apiKey: string;
|
|
7
|
-
/** Your app ID from the
|
|
7
|
+
/** Your app ID from the Anomira dashboard */
|
|
8
8
|
appId: string;
|
|
9
9
|
/**
|
|
10
10
|
* Ingest endpoint URL.
|
|
@@ -13,7 +13,7 @@ interface SentinelConfig {
|
|
|
13
13
|
ingestUrl?: string;
|
|
14
14
|
/**
|
|
15
15
|
* Geo-lookup endpoint URL for client-side geo-velocity checks.
|
|
16
|
-
* Should point to your
|
|
16
|
+
* Should point to your Anomira ingest service geo endpoint.
|
|
17
17
|
* If not provided, client-side geo-velocity is skipped (server-side still runs).
|
|
18
18
|
* @example "https://ingest.anomira.io/v1/geo"
|
|
19
19
|
*/
|
|
@@ -40,7 +40,7 @@ interface SentinelConfig {
|
|
|
40
40
|
debug?: boolean;
|
|
41
41
|
/**
|
|
42
42
|
* If true, automatically intercept console.log/info/warn/error/debug
|
|
43
|
-
* and forward them to the
|
|
43
|
+
* and forward them to the Anomira Logs dashboard.
|
|
44
44
|
* Existing console output is preserved — logs still print to your terminal.
|
|
45
45
|
* @default false
|
|
46
46
|
*/
|
|
@@ -116,6 +116,14 @@ type EventNameValue = typeof EventName[keyof typeof EventName];
|
|
|
116
116
|
type FirewallField = "url" | "body" | "header" | "user_agent" | "ip";
|
|
117
117
|
type FirewallOperator = "contains" | "equals" | "starts_with" | "ends_with" | "regex";
|
|
118
118
|
type FirewallAction = "block" | "flag";
|
|
119
|
+
interface EndpointDeclaration {
|
|
120
|
+
/** HTTP method, e.g. "GET". Use "*" to match any method. */
|
|
121
|
+
method: string;
|
|
122
|
+
/** Express-style path, e.g. "/api/users/:id" — colons are normalized automatically. */
|
|
123
|
+
path: string;
|
|
124
|
+
/** Whether this endpoint requires authentication. Defaults to true. */
|
|
125
|
+
auth?: boolean;
|
|
126
|
+
}
|
|
119
127
|
interface FirewallRule {
|
|
120
128
|
id: string;
|
|
121
129
|
field: FirewallField;
|
|
@@ -127,25 +135,25 @@ interface FirewallRule {
|
|
|
127
135
|
}
|
|
128
136
|
|
|
129
137
|
/**
|
|
130
|
-
*
|
|
138
|
+
* AnomiraClient — the core SDK object.
|
|
131
139
|
*
|
|
132
140
|
* Instantiate once and reuse across your application:
|
|
133
141
|
*
|
|
134
142
|
* ```ts
|
|
135
|
-
* import {
|
|
143
|
+
* import { Anomira } from "@anomira/node-sdk";
|
|
136
144
|
*
|
|
137
|
-
* export const sentinel = new
|
|
145
|
+
* export const sentinel = new Anomira({
|
|
138
146
|
* apiKey: process.env.SENTINEL_API_KEY!,
|
|
139
147
|
* appId: process.env.SENTINEL_APP_ID!,
|
|
140
148
|
* });
|
|
141
149
|
* ```
|
|
142
150
|
*/
|
|
143
|
-
declare class
|
|
151
|
+
declare class AnomiraClient {
|
|
144
152
|
#private;
|
|
145
|
-
readonly config: Required<Omit<
|
|
146
|
-
getUserId: NonNullable<
|
|
147
|
-
getIp: NonNullable<
|
|
148
|
-
detect: Required<NonNullable<
|
|
153
|
+
readonly config: Required<Omit<AnomiraConfig, "getUserId" | "getIp" | "detect">> & {
|
|
154
|
+
getUserId: NonNullable<AnomiraConfig["getUserId"]>;
|
|
155
|
+
getIp: NonNullable<AnomiraConfig["getIp"]>;
|
|
156
|
+
detect: Required<NonNullable<AnomiraConfig["detect"]>>;
|
|
149
157
|
captureConsole: boolean;
|
|
150
158
|
service: string;
|
|
151
159
|
};
|
|
@@ -163,7 +171,7 @@ declare class SentinelClient {
|
|
|
163
171
|
private readonly _origLog;
|
|
164
172
|
private readonly _origWarn;
|
|
165
173
|
private readonly _origError;
|
|
166
|
-
constructor(config:
|
|
174
|
+
constructor(config: AnomiraConfig);
|
|
167
175
|
/**
|
|
168
176
|
* Track a custom security event.
|
|
169
177
|
*
|
|
@@ -230,7 +238,7 @@ declare class SentinelClient {
|
|
|
230
238
|
meta?: Record<string, unknown>;
|
|
231
239
|
}): void;
|
|
232
240
|
/**
|
|
233
|
-
* Send a structured log entry to the
|
|
241
|
+
* Send a structured log entry to the Anomira Logs dashboard.
|
|
234
242
|
*
|
|
235
243
|
* ```ts
|
|
236
244
|
* sentinel.log("info", "User registered", { userId: user.id });
|
|
@@ -241,6 +249,21 @@ declare class SentinelClient {
|
|
|
241
249
|
log(level: "debug" | "info" | "warn" | "error" | "fatal", message: string, meta?: Record<string, unknown> & {
|
|
242
250
|
service?: string;
|
|
243
251
|
}): void;
|
|
252
|
+
/**
|
|
253
|
+
* Declare your API's known endpoints so Anomira can flag undiscovered
|
|
254
|
+
* traffic as shadow endpoints.
|
|
255
|
+
*
|
|
256
|
+
* Call this once on startup after your routes are registered:
|
|
257
|
+
*
|
|
258
|
+
* ```ts
|
|
259
|
+
* await sentinel.declareEndpoints([
|
|
260
|
+
* { method: "GET", path: "/api/users/:id", auth: true },
|
|
261
|
+
* { method: "POST", path: "/api/orders", auth: true },
|
|
262
|
+
* { method: "GET", path: "/api/health", auth: false },
|
|
263
|
+
* ]);
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declareEndpoints(endpoints: EndpointDeclaration[]): Promise<void>;
|
|
244
267
|
/**
|
|
245
268
|
* Flush all pending events immediately.
|
|
246
269
|
* Useful before a graceful shutdown outside of the process lifecycle hooks.
|
|
@@ -278,10 +301,10 @@ declare class SentinelClient {
|
|
|
278
301
|
*
|
|
279
302
|
* ```ts
|
|
280
303
|
* import express from "express";
|
|
281
|
-
* import {
|
|
304
|
+
* import { Anomira } from "@sentinelapi/node-sdk";
|
|
282
305
|
*
|
|
283
306
|
* const app = express();
|
|
284
|
-
* const sentinel = new
|
|
307
|
+
* const sentinel = new Anomira({ apiKey: "...", appId: "..." });
|
|
285
308
|
*
|
|
286
309
|
* app.use(sentinel.express()); // auto-instrument all routes
|
|
287
310
|
*
|
|
@@ -290,7 +313,7 @@ declare class SentinelClient {
|
|
|
290
313
|
* app.use(createExpressMiddleware(sentinel));
|
|
291
314
|
* ```
|
|
292
315
|
*/
|
|
293
|
-
declare function createExpressMiddleware(client:
|
|
316
|
+
declare function createExpressMiddleware(client: AnomiraClient): (req: Request, res: Response, next: NextFunction) => void;
|
|
294
317
|
|
|
295
318
|
/**
|
|
296
319
|
* Typed Fastify plugin — exported separately for projects that want
|
|
@@ -304,14 +327,14 @@ declare function createExpressMiddleware(client: SentinelClient): (req: Request,
|
|
|
304
327
|
*
|
|
305
328
|
* ```ts
|
|
306
329
|
* import Fastify from "fastify";
|
|
307
|
-
* import {
|
|
330
|
+
* import { Anomira } from "@sentinelapi/node-sdk";
|
|
308
331
|
*
|
|
309
332
|
* const app = Fastify();
|
|
310
|
-
* const sentinel = new
|
|
333
|
+
* const sentinel = new Anomira({ apiKey: "...", appId: "..." });
|
|
311
334
|
*
|
|
312
335
|
* await app.register(sentinel.fastify());
|
|
313
336
|
* ```
|
|
314
337
|
*/
|
|
315
|
-
declare function createFastifyPlugin(client:
|
|
338
|
+
declare function createFastifyPlugin(client: AnomiraClient): FastifyPluginAsync;
|
|
316
339
|
|
|
317
|
-
export { EventName, type EventNameValue, type IngestPayload, type SdkEvent,
|
|
340
|
+
export { AnomiraClient as Anomira, type AnomiraConfig, type EndpointDeclaration, EventName, type EventNameValue, type IngestPayload, type SdkEvent, AnomiraClient as SentinelAPI, type AnomiraConfig as SentinelConfig, createExpressMiddleware, createFastifyPlugin, AnomiraClient as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
2
|
import { FastifyPluginAsync } from 'fastify';
|
|
3
3
|
|
|
4
|
-
interface
|
|
5
|
-
/** SDK API key — get this from the
|
|
4
|
+
interface AnomiraConfig {
|
|
5
|
+
/** SDK API key — get this from the Anomira dashboard */
|
|
6
6
|
apiKey: string;
|
|
7
|
-
/** Your app ID from the
|
|
7
|
+
/** Your app ID from the Anomira dashboard */
|
|
8
8
|
appId: string;
|
|
9
9
|
/**
|
|
10
10
|
* Ingest endpoint URL.
|
|
@@ -13,7 +13,7 @@ interface SentinelConfig {
|
|
|
13
13
|
ingestUrl?: string;
|
|
14
14
|
/**
|
|
15
15
|
* Geo-lookup endpoint URL for client-side geo-velocity checks.
|
|
16
|
-
* Should point to your
|
|
16
|
+
* Should point to your Anomira ingest service geo endpoint.
|
|
17
17
|
* If not provided, client-side geo-velocity is skipped (server-side still runs).
|
|
18
18
|
* @example "https://ingest.anomira.io/v1/geo"
|
|
19
19
|
*/
|
|
@@ -40,7 +40,7 @@ interface SentinelConfig {
|
|
|
40
40
|
debug?: boolean;
|
|
41
41
|
/**
|
|
42
42
|
* If true, automatically intercept console.log/info/warn/error/debug
|
|
43
|
-
* and forward them to the
|
|
43
|
+
* and forward them to the Anomira Logs dashboard.
|
|
44
44
|
* Existing console output is preserved — logs still print to your terminal.
|
|
45
45
|
* @default false
|
|
46
46
|
*/
|
|
@@ -116,6 +116,14 @@ type EventNameValue = typeof EventName[keyof typeof EventName];
|
|
|
116
116
|
type FirewallField = "url" | "body" | "header" | "user_agent" | "ip";
|
|
117
117
|
type FirewallOperator = "contains" | "equals" | "starts_with" | "ends_with" | "regex";
|
|
118
118
|
type FirewallAction = "block" | "flag";
|
|
119
|
+
interface EndpointDeclaration {
|
|
120
|
+
/** HTTP method, e.g. "GET". Use "*" to match any method. */
|
|
121
|
+
method: string;
|
|
122
|
+
/** Express-style path, e.g. "/api/users/:id" — colons are normalized automatically. */
|
|
123
|
+
path: string;
|
|
124
|
+
/** Whether this endpoint requires authentication. Defaults to true. */
|
|
125
|
+
auth?: boolean;
|
|
126
|
+
}
|
|
119
127
|
interface FirewallRule {
|
|
120
128
|
id: string;
|
|
121
129
|
field: FirewallField;
|
|
@@ -127,25 +135,25 @@ interface FirewallRule {
|
|
|
127
135
|
}
|
|
128
136
|
|
|
129
137
|
/**
|
|
130
|
-
*
|
|
138
|
+
* AnomiraClient — the core SDK object.
|
|
131
139
|
*
|
|
132
140
|
* Instantiate once and reuse across your application:
|
|
133
141
|
*
|
|
134
142
|
* ```ts
|
|
135
|
-
* import {
|
|
143
|
+
* import { Anomira } from "@anomira/node-sdk";
|
|
136
144
|
*
|
|
137
|
-
* export const sentinel = new
|
|
145
|
+
* export const sentinel = new Anomira({
|
|
138
146
|
* apiKey: process.env.SENTINEL_API_KEY!,
|
|
139
147
|
* appId: process.env.SENTINEL_APP_ID!,
|
|
140
148
|
* });
|
|
141
149
|
* ```
|
|
142
150
|
*/
|
|
143
|
-
declare class
|
|
151
|
+
declare class AnomiraClient {
|
|
144
152
|
#private;
|
|
145
|
-
readonly config: Required<Omit<
|
|
146
|
-
getUserId: NonNullable<
|
|
147
|
-
getIp: NonNullable<
|
|
148
|
-
detect: Required<NonNullable<
|
|
153
|
+
readonly config: Required<Omit<AnomiraConfig, "getUserId" | "getIp" | "detect">> & {
|
|
154
|
+
getUserId: NonNullable<AnomiraConfig["getUserId"]>;
|
|
155
|
+
getIp: NonNullable<AnomiraConfig["getIp"]>;
|
|
156
|
+
detect: Required<NonNullable<AnomiraConfig["detect"]>>;
|
|
149
157
|
captureConsole: boolean;
|
|
150
158
|
service: string;
|
|
151
159
|
};
|
|
@@ -163,7 +171,7 @@ declare class SentinelClient {
|
|
|
163
171
|
private readonly _origLog;
|
|
164
172
|
private readonly _origWarn;
|
|
165
173
|
private readonly _origError;
|
|
166
|
-
constructor(config:
|
|
174
|
+
constructor(config: AnomiraConfig);
|
|
167
175
|
/**
|
|
168
176
|
* Track a custom security event.
|
|
169
177
|
*
|
|
@@ -230,7 +238,7 @@ declare class SentinelClient {
|
|
|
230
238
|
meta?: Record<string, unknown>;
|
|
231
239
|
}): void;
|
|
232
240
|
/**
|
|
233
|
-
* Send a structured log entry to the
|
|
241
|
+
* Send a structured log entry to the Anomira Logs dashboard.
|
|
234
242
|
*
|
|
235
243
|
* ```ts
|
|
236
244
|
* sentinel.log("info", "User registered", { userId: user.id });
|
|
@@ -241,6 +249,21 @@ declare class SentinelClient {
|
|
|
241
249
|
log(level: "debug" | "info" | "warn" | "error" | "fatal", message: string, meta?: Record<string, unknown> & {
|
|
242
250
|
service?: string;
|
|
243
251
|
}): void;
|
|
252
|
+
/**
|
|
253
|
+
* Declare your API's known endpoints so Anomira can flag undiscovered
|
|
254
|
+
* traffic as shadow endpoints.
|
|
255
|
+
*
|
|
256
|
+
* Call this once on startup after your routes are registered:
|
|
257
|
+
*
|
|
258
|
+
* ```ts
|
|
259
|
+
* await sentinel.declareEndpoints([
|
|
260
|
+
* { method: "GET", path: "/api/users/:id", auth: true },
|
|
261
|
+
* { method: "POST", path: "/api/orders", auth: true },
|
|
262
|
+
* { method: "GET", path: "/api/health", auth: false },
|
|
263
|
+
* ]);
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declareEndpoints(endpoints: EndpointDeclaration[]): Promise<void>;
|
|
244
267
|
/**
|
|
245
268
|
* Flush all pending events immediately.
|
|
246
269
|
* Useful before a graceful shutdown outside of the process lifecycle hooks.
|
|
@@ -278,10 +301,10 @@ declare class SentinelClient {
|
|
|
278
301
|
*
|
|
279
302
|
* ```ts
|
|
280
303
|
* import express from "express";
|
|
281
|
-
* import {
|
|
304
|
+
* import { Anomira } from "@sentinelapi/node-sdk";
|
|
282
305
|
*
|
|
283
306
|
* const app = express();
|
|
284
|
-
* const sentinel = new
|
|
307
|
+
* const sentinel = new Anomira({ apiKey: "...", appId: "..." });
|
|
285
308
|
*
|
|
286
309
|
* app.use(sentinel.express()); // auto-instrument all routes
|
|
287
310
|
*
|
|
@@ -290,7 +313,7 @@ declare class SentinelClient {
|
|
|
290
313
|
* app.use(createExpressMiddleware(sentinel));
|
|
291
314
|
* ```
|
|
292
315
|
*/
|
|
293
|
-
declare function createExpressMiddleware(client:
|
|
316
|
+
declare function createExpressMiddleware(client: AnomiraClient): (req: Request, res: Response, next: NextFunction) => void;
|
|
294
317
|
|
|
295
318
|
/**
|
|
296
319
|
* Typed Fastify plugin — exported separately for projects that want
|
|
@@ -304,14 +327,14 @@ declare function createExpressMiddleware(client: SentinelClient): (req: Request,
|
|
|
304
327
|
*
|
|
305
328
|
* ```ts
|
|
306
329
|
* import Fastify from "fastify";
|
|
307
|
-
* import {
|
|
330
|
+
* import { Anomira } from "@sentinelapi/node-sdk";
|
|
308
331
|
*
|
|
309
332
|
* const app = Fastify();
|
|
310
|
-
* const sentinel = new
|
|
333
|
+
* const sentinel = new Anomira({ apiKey: "...", appId: "..." });
|
|
311
334
|
*
|
|
312
335
|
* await app.register(sentinel.fastify());
|
|
313
336
|
* ```
|
|
314
337
|
*/
|
|
315
|
-
declare function createFastifyPlugin(client:
|
|
338
|
+
declare function createFastifyPlugin(client: AnomiraClient): FastifyPluginAsync;
|
|
316
339
|
|
|
317
|
-
export { EventName, type EventNameValue, type IngestPayload, type SdkEvent,
|
|
340
|
+
export { AnomiraClient as Anomira, type AnomiraConfig, type EndpointDeclaration, EventName, type EventNameValue, type IngestPayload, type SdkEvent, AnomiraClient as SentinelAPI, type AnomiraConfig as SentinelConfig, createExpressMiddleware, createFastifyPlugin, AnomiraClient as default };
|
package/dist/index.js
CHANGED
|
@@ -105,10 +105,10 @@ var EventBuffer = class {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
log(...args) {
|
|
108
|
-
if (this.opts.debug) console.log("[
|
|
108
|
+
if (this.opts.debug) console.log("[Anomira]", ...args);
|
|
109
109
|
}
|
|
110
110
|
warn(...args) {
|
|
111
|
-
console.warn("[
|
|
111
|
+
console.warn("[Anomira]", ...args);
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
114
|
function sleep(ms) {
|
|
@@ -195,17 +195,15 @@ async function checkGeoVelocity(userId, ip, tsMs, lookupUrl) {
|
|
|
195
195
|
|
|
196
196
|
// src/sensitive.ts
|
|
197
197
|
var PATTERNS = [
|
|
198
|
-
// ── Cryptographic keys
|
|
198
|
+
// ── Cryptographic private keys ────────────────────────────────────────────
|
|
199
|
+
// NOTE: -----BEGIN CERTIFICATE----- is intentionally excluded — public
|
|
200
|
+
// certificates are designed to be public and committing them is correct.
|
|
201
|
+
// Only PRIVATE keys are dangerous.
|
|
199
202
|
{
|
|
200
203
|
type: "private_key",
|
|
201
204
|
label: "Private Key",
|
|
202
205
|
regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/
|
|
203
206
|
},
|
|
204
|
-
{
|
|
205
|
-
type: "certificate",
|
|
206
|
-
label: "Certificate / Public Key",
|
|
207
|
-
regex: /-----BEGIN CERTIFICATE-----/
|
|
208
|
-
},
|
|
209
207
|
// ── Cloud provider credentials ────────────────────────────────────────────
|
|
210
208
|
{
|
|
211
209
|
// AWS access key ID — highly specific, almost no false positives
|
|
@@ -338,11 +336,12 @@ var PATTERNS = [
|
|
|
338
336
|
},
|
|
339
337
|
// ── Nigeria-specific PII ──────────────────────────────────────────────────
|
|
340
338
|
{
|
|
341
|
-
// BVN / NIN: 11 digits, first digit 1-9 (not a phone starting with 0)
|
|
342
|
-
//
|
|
339
|
+
// BVN / NIN: 11 digits, first digit 1-9 (not a phone number starting with 0)
|
|
340
|
+
// Exclude: inside URLs (preceded by / : @ - %), hex strings (followed by a-f),
|
|
341
|
+
// and UUIDs/hashes (surrounded by alphanumeric chars)
|
|
343
342
|
type: "bvn",
|
|
344
343
|
label: "BVN / NIN",
|
|
345
|
-
regex: /(
|
|
344
|
+
regex: /(?<![/\-:@%=a-fA-F\w])[1-9]\d{10}(?![a-fA-F\d])/
|
|
346
345
|
},
|
|
347
346
|
{
|
|
348
347
|
// Nigerian phone numbers: 080x, 081x, 070x, 090x, 091x — or with +234 prefix
|
|
@@ -409,7 +408,7 @@ var DEFAULT_INGEST_URL = "https://ingest.anomira.io/v1/events";
|
|
|
409
408
|
var DEFAULT_BATCH_SIZE = 100;
|
|
410
409
|
var DEFAULT_FLUSH_MS = 5e3;
|
|
411
410
|
var DEFAULT_MAX_RETRIES = 3;
|
|
412
|
-
var
|
|
411
|
+
var AnomiraClient = class {
|
|
413
412
|
constructor(config) {
|
|
414
413
|
this.logBuffer = [];
|
|
415
414
|
this.logFlushTimer = null;
|
|
@@ -508,7 +507,7 @@ var SentinelClient = class {
|
|
|
508
507
|
console[method] = (...args) => {
|
|
509
508
|
original(...args);
|
|
510
509
|
const first = args[0];
|
|
511
|
-
if (typeof first === "string" && first.startsWith("[
|
|
510
|
+
if (typeof first === "string" && first.startsWith("[Anomira]")) return;
|
|
512
511
|
const message = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
513
512
|
const ctx = requestContext.getStore();
|
|
514
513
|
this.log(level, message, {
|
|
@@ -532,7 +531,7 @@ var SentinelClient = class {
|
|
|
532
531
|
const data = await res.json();
|
|
533
532
|
this.blockedIpCache = new Set(data.ips ?? []);
|
|
534
533
|
if (this.config.debug && this.blockedIpCache.size > 0) {
|
|
535
|
-
this._origLog(`[
|
|
534
|
+
this._origLog(`[Anomira] blocklist refreshed \u2014 ${this.blockedIpCache.size} blocked IPs`);
|
|
536
535
|
}
|
|
537
536
|
} catch {
|
|
538
537
|
}
|
|
@@ -560,7 +559,7 @@ var SentinelClient = class {
|
|
|
560
559
|
})() : void 0
|
|
561
560
|
}));
|
|
562
561
|
if (this.config.debug && this.compiledRules.length > 0) {
|
|
563
|
-
this._origLog(`[
|
|
562
|
+
this._origLog(`[Anomira] firewall rules refreshed \u2014 ${this.compiledRules.length} active rules`);
|
|
564
563
|
}
|
|
565
564
|
} catch {
|
|
566
565
|
}
|
|
@@ -611,7 +610,7 @@ var SentinelClient = class {
|
|
|
611
610
|
signal: AbortSignal.timeout(8e3)
|
|
612
611
|
});
|
|
613
612
|
if (this.config.debug) {
|
|
614
|
-
this._origLog(`[
|
|
613
|
+
this._origLog(`[Anomira] [logs] \u2705 sent ${batch.length} log entries (${res.status})`);
|
|
615
614
|
}
|
|
616
615
|
} catch {
|
|
617
616
|
this.logBuffer.unshift(...batch);
|
|
@@ -630,24 +629,24 @@ var SentinelClient = class {
|
|
|
630
629
|
signal: AbortSignal.timeout(5e3)
|
|
631
630
|
});
|
|
632
631
|
if (res.status >= 300 && res.status < 400) {
|
|
633
|
-
this._origWarn(`[
|
|
632
|
+
this._origWarn(`[Anomira] \u274C Wrong ingest URL \u2014 got redirect to ${res.headers.get("location")}. Check SENTINEL_INGEST_URL.`);
|
|
634
633
|
return;
|
|
635
634
|
}
|
|
636
635
|
if (res.ok) {
|
|
637
|
-
this._origLog(`[
|
|
636
|
+
this._origLog(`[Anomira] \u2705 Connected (appId: ${this.config.appId.slice(0, 8)}\u2026)`);
|
|
638
637
|
return;
|
|
639
638
|
}
|
|
640
639
|
if (res.status === 401) {
|
|
641
|
-
this._origWarn("[
|
|
640
|
+
this._origWarn("[Anomira] \u274C Invalid API key \u2014 check your SENTINEL_API_KEY");
|
|
642
641
|
return;
|
|
643
642
|
}
|
|
644
643
|
if (res.status === 403) {
|
|
645
|
-
this._origWarn("[
|
|
644
|
+
this._origWarn("[Anomira] \u274C App not found or appId mismatch \u2014 check your SENTINEL_APP_ID");
|
|
646
645
|
return;
|
|
647
646
|
}
|
|
648
|
-
this._origWarn(`[
|
|
647
|
+
this._origWarn(`[Anomira] \u26A0\uFE0F Ingest returned HTTP ${res.status} \u2014 check your configuration`);
|
|
649
648
|
} catch {
|
|
650
|
-
this._origWarn("[
|
|
649
|
+
this._origWarn("[Anomira] \u26A0\uFE0F Could not reach ingest endpoint \u2014 check SENTINEL_INGEST_URL (current: " + this.config.ingestUrl + ")");
|
|
651
650
|
}
|
|
652
651
|
}
|
|
653
652
|
// ─── Public API ────────────────────────────────────────────────────────────
|
|
@@ -744,7 +743,7 @@ var SentinelClient = class {
|
|
|
744
743
|
});
|
|
745
744
|
}
|
|
746
745
|
/**
|
|
747
|
-
* Send a structured log entry to the
|
|
746
|
+
* Send a structured log entry to the Anomira Logs dashboard.
|
|
748
747
|
*
|
|
749
748
|
* ```ts
|
|
750
749
|
* sentinel.log("info", "User registered", { userId: user.id });
|
|
@@ -760,16 +759,47 @@ var SentinelClient = class {
|
|
|
760
759
|
rest["sensitiveLeaks"] = leaks.map((l) => l.type);
|
|
761
760
|
if (this.config.debug) {
|
|
762
761
|
this._origWarn(
|
|
763
|
-
`[
|
|
762
|
+
`[Anomira] \u26A0\uFE0F Sensitive data in log (${leaks.map((l) => l.label).join(", ")}): "${message.slice(0, 60)}\u2026"`
|
|
764
763
|
);
|
|
765
764
|
}
|
|
766
765
|
}
|
|
767
766
|
this.logBuffer.push({ level, service: service ?? this.config.service, message, meta: rest, ts: Date.now() });
|
|
768
767
|
if (this.config.debug && leaks.length === 0) {
|
|
769
|
-
this._origLog(`[
|
|
768
|
+
this._origLog(`[Anomira] log:${level} ${message}`);
|
|
770
769
|
}
|
|
771
770
|
if (this.logBuffer.length >= 50) void this.#flushLogs();
|
|
772
771
|
}
|
|
772
|
+
/**
|
|
773
|
+
* Declare your API's known endpoints so Anomira can flag undiscovered
|
|
774
|
+
* traffic as shadow endpoints.
|
|
775
|
+
*
|
|
776
|
+
* Call this once on startup after your routes are registered:
|
|
777
|
+
*
|
|
778
|
+
* ```ts
|
|
779
|
+
* await sentinel.declareEndpoints([
|
|
780
|
+
* { method: "GET", path: "/api/users/:id", auth: true },
|
|
781
|
+
* { method: "POST", path: "/api/orders", auth: true },
|
|
782
|
+
* { method: "GET", path: "/api/health", auth: false },
|
|
783
|
+
* ]);
|
|
784
|
+
* ```
|
|
785
|
+
*/
|
|
786
|
+
async declareEndpoints(endpoints) {
|
|
787
|
+
if (this.disabled || endpoints.length === 0) return;
|
|
788
|
+
const url = this.config.ingestUrl.replace(/\/v1\/events$/, "/v1/declare-endpoints");
|
|
789
|
+
try {
|
|
790
|
+
await fetch(url, {
|
|
791
|
+
method: "POST",
|
|
792
|
+
headers: {
|
|
793
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
794
|
+
"Content-Type": "application/json",
|
|
795
|
+
"User-Agent": `@anomira/node-sdk/0.1.0`
|
|
796
|
+
},
|
|
797
|
+
body: JSON.stringify({ appId: this.config.appId, endpoints }),
|
|
798
|
+
signal: AbortSignal.timeout(1e4)
|
|
799
|
+
});
|
|
800
|
+
} catch {
|
|
801
|
+
}
|
|
802
|
+
}
|
|
773
803
|
/**
|
|
774
804
|
* Flush all pending events immediately.
|
|
775
805
|
* Useful before a graceful shutdown outside of the process lifecycle hooks.
|
|
@@ -1110,6 +1140,6 @@ function createFastifyPlugin2(client) {
|
|
|
1110
1140
|
};
|
|
1111
1141
|
}
|
|
1112
1142
|
|
|
1113
|
-
export { EventName,
|
|
1143
|
+
export { AnomiraClient as Anomira, EventName, AnomiraClient as SentinelAPI, createExpressMiddleware2 as createExpressMiddleware, createFastifyPlugin2 as createFastifyPlugin, AnomiraClient as default };
|
|
1114
1144
|
//# sourceMappingURL=index.js.map
|
|
1115
1145
|
//# sourceMappingURL=index.js.map
|