@huloglobal/vendure-licence-sdk 0.1.0 → 0.3.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/CHANGELOG.md CHANGED
@@ -4,7 +4,49 @@ All notable changes to `@huloglobal/vendure-licence-sdk` are documented here. Th
4
4
  format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and
5
5
  this project adheres to [semantic versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [0.1.0] — Unreleased
7
+ ## [0.3.0]
8
+
9
+ ### Added
10
+ - `startRetentionSweeper({ ... })` — schedules a recurring
11
+ `DELETE FROM <table> WHERE createdAt < ?` sweep that prunes rows
12
+ older than `options.days`. Optional `options.maxRows` adds a hard cap
13
+ on total rows (oldest pruned first when exceeded). `options.days = 0`
14
+ or `null` keeps everything (no pruning). Sweeper interval defaults to
15
+ 24h, is `.unref()`d so it doesn't block shutdown, and runs first 60s
16
+ after start so boot stays snappy.
17
+ - Shared security primitives module (`security.ts`), exported from the
18
+ package root:
19
+ - `verifyHmacSha256(body, signature, secret)` — timing-safe HMAC-SHA256
20
+ verification for webhooks. Tolerates the GitHub-style `sha256=`
21
+ prefix.
22
+ - `signValue(value, secret)` / `verifySignedValue(signed, secret)` —
23
+ HMAC tag a string so we can detect tampering when it round-trips
24
+ through a cookie or URL parameter. 64-bit tag keeps cookies short.
25
+ - `RateLimiter` — token-bucket rate limiter with an LRU cap on the
26
+ keyspace so a flood of keys can't grow memory.
27
+ - `applySecurityHeaders(res, { strict })` — recommended baseline of
28
+ security headers (X-Content-Type-Options, X-Frame-Options,
29
+ Referrer-Policy, Permissions-Policy, Cross-Origin-Resource-Policy).
30
+ `strict` adds a tight CSP for JSON / API endpoints.
31
+ - `isUrlOnAllowlist(url, allowedDomains)` — guards click redirectors
32
+ against being used as an open redirector. Supports wildcard suffixes
33
+ (`*.example.com`).
34
+ - `hashIp(ip, salt)` — sha256 IP hashing with a per-install salt.
35
+ - `randomToken(bytes)` — URL-safe random tokens for secrets / one-shot
36
+ nonces.
37
+
38
+ ## [0.2.0]
39
+
40
+ ### Added
41
+ - **`UpdateChecker`** — polls the public npm registry every 24h for the
42
+ package's latest version. Exposes `current`, `latest`, `updateAvailable`,
43
+ `isMajor`, `lastCheckedAt`, `lastError`. Soft-fails on network errors
44
+ (keeps the previous cached value in memory). Each consuming plugin
45
+ surfaces its status via a `/status` endpoint so the admin dashboard
46
+ can render an "update available" banner.
47
+ - `UpdateStatus` exported type.
48
+
49
+ ## [0.1.0]
8
50
 
9
51
  ### Added
10
52
  - Offline RSA-SHA256 JWT licence verification (`verifyLicence`).
package/dist/index.d.ts CHANGED
@@ -21,4 +21,7 @@
21
21
  export { verifyLicence } from './verify';
22
22
  export { LicenceStatus, LicencePayload, VerifyLicenceOptions } from './types';
23
23
  export { RevocationChecker } from './revocation';
24
+ export { UpdateChecker, UpdateStatus } from './update-check';
25
+ export { verifyHmacSha256, signValue, verifySignedValue, RateLimiter, RateLimiterOptions, applySecurityHeaders, isUrlOnAllowlist, hashIp, randomToken, } from './security';
26
+ export { startRetentionSweeper, RetentionOptions } from './retention';
24
27
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EACH,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,WAAW,EACX,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,MAAM,EACN,WAAW,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -20,9 +20,22 @@
20
20
  * tier. The end-user can always upgrade by supplying a valid key.
21
21
  */
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.RevocationChecker = exports.verifyLicence = void 0;
23
+ exports.startRetentionSweeper = exports.randomToken = exports.hashIp = exports.isUrlOnAllowlist = exports.applySecurityHeaders = exports.RateLimiter = exports.verifySignedValue = exports.signValue = exports.verifyHmacSha256 = exports.UpdateChecker = exports.RevocationChecker = exports.verifyLicence = void 0;
24
24
  var verify_1 = require("./verify");
25
25
  Object.defineProperty(exports, "verifyLicence", { enumerable: true, get: function () { return verify_1.verifyLicence; } });
26
26
  var revocation_1 = require("./revocation");
27
27
  Object.defineProperty(exports, "RevocationChecker", { enumerable: true, get: function () { return revocation_1.RevocationChecker; } });
28
+ var update_check_1 = require("./update-check");
29
+ Object.defineProperty(exports, "UpdateChecker", { enumerable: true, get: function () { return update_check_1.UpdateChecker; } });
30
+ var security_1 = require("./security");
31
+ Object.defineProperty(exports, "verifyHmacSha256", { enumerable: true, get: function () { return security_1.verifyHmacSha256; } });
32
+ Object.defineProperty(exports, "signValue", { enumerable: true, get: function () { return security_1.signValue; } });
33
+ Object.defineProperty(exports, "verifySignedValue", { enumerable: true, get: function () { return security_1.verifySignedValue; } });
34
+ Object.defineProperty(exports, "RateLimiter", { enumerable: true, get: function () { return security_1.RateLimiter; } });
35
+ Object.defineProperty(exports, "applySecurityHeaders", { enumerable: true, get: function () { return security_1.applySecurityHeaders; } });
36
+ Object.defineProperty(exports, "isUrlOnAllowlist", { enumerable: true, get: function () { return security_1.isUrlOnAllowlist; } });
37
+ Object.defineProperty(exports, "hashIp", { enumerable: true, get: function () { return security_1.hashIp; } });
38
+ Object.defineProperty(exports, "randomToken", { enumerable: true, get: function () { return security_1.randomToken; } });
39
+ var retention_1 = require("./retention");
40
+ Object.defineProperty(exports, "startRetentionSweeper", { enumerable: true, get: function () { return retention_1.startRetentionSweeper; } });
28
41
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;;AAEH,mCAAyC;AAAhC,uGAAA,aAAa,OAAA;AAEtB,2CAAiD;AAAxC,+GAAA,iBAAiB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;;AAEH,mCAAyC;AAAhC,uGAAA,aAAa,OAAA;AAEtB,2CAAiD;AAAxC,+GAAA,iBAAiB,OAAA;AAC1B,+CAA6D;AAApD,6GAAA,aAAa,OAAA;AACtB,uCAUoB;AAThB,4GAAA,gBAAgB,OAAA;AAChB,qGAAA,SAAS,OAAA;AACT,6GAAA,iBAAiB,OAAA;AACjB,uGAAA,WAAW,OAAA;AAEX,gHAAA,oBAAoB,OAAA;AACpB,4GAAA,gBAAgB,OAAA;AAChB,kGAAA,MAAM,OAAA;AACN,uGAAA,WAAW,OAAA;AAEf,yCAAsE;AAA7D,kHAAA,qBAAqB,OAAA"}
@@ -0,0 +1,46 @@
1
+ export interface RetentionOptions {
2
+ /**
3
+ * Maximum age in days. Rows older than this are deleted by the
4
+ * scheduled sweeper. `null` or `0` keeps everything (no pruning).
5
+ */
6
+ days: number | null;
7
+ /**
8
+ * Optional hard cap on total rows. When the table exceeds this,
9
+ * the oldest rows are pruned regardless of age. `null` disables
10
+ * this safety valve.
11
+ */
12
+ maxRows?: number | null;
13
+ /**
14
+ * How often the sweeper runs, in milliseconds. Default 24h. The
15
+ * sweeper is debounced to one run per process and is `.unref()`d
16
+ * so it doesn't keep the Node event loop alive.
17
+ */
18
+ sweepIntervalMs?: number;
19
+ }
20
+ /**
21
+ * Schedule a recurring `DELETE FROM <table> WHERE createdAt < ?` sweep
22
+ * that prunes rows older than `opts.days`. Optionally also caps total
23
+ * row count by `opts.maxRows`.
24
+ *
25
+ * startRetentionSweeper({
26
+ * getQueryRunner: () => connection.rawConnection,
27
+ * table: 'email_log',
28
+ * dateColumn: 'createdAt',
29
+ * options: { days: 180 },
30
+ * });
31
+ *
32
+ * Returns a stop function so tests can shut the sweeper down.
33
+ */
34
+ export declare function startRetentionSweeper(input: {
35
+ /** Run a raw SQL query; returns a Promise of any result. Plugins
36
+ * typically pass `() => connection.rawConnection`. */
37
+ getConnection: () => {
38
+ query: (sql: string, params?: any[]) => Promise<any>;
39
+ } | null;
40
+ table: string;
41
+ dateColumn?: string;
42
+ options: RetentionOptions;
43
+ /** Optional human-readable plugin name used in log lines. */
44
+ label?: string;
45
+ }): () => void;
46
+ //# sourceMappingURL=retention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.d.ts","sourceRoot":"","sources":["../src/retention.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC7B;;;OAGG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IACzC;2DACuD;IACvD,aAAa,EAAE,MAAM;QAAE,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;IACrF,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,IAAI,CA4Db"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startRetentionSweeper = startRetentionSweeper;
4
+ const core_1 = require("@vendure/core");
5
+ const loggerCtx = 'LicenceSdk:Retention';
6
+ /**
7
+ * Schedule a recurring `DELETE FROM <table> WHERE createdAt < ?` sweep
8
+ * that prunes rows older than `opts.days`. Optionally also caps total
9
+ * row count by `opts.maxRows`.
10
+ *
11
+ * startRetentionSweeper({
12
+ * getQueryRunner: () => connection.rawConnection,
13
+ * table: 'email_log',
14
+ * dateColumn: 'createdAt',
15
+ * options: { days: 180 },
16
+ * });
17
+ *
18
+ * Returns a stop function so tests can shut the sweeper down.
19
+ */
20
+ function startRetentionSweeper(input) {
21
+ var _a;
22
+ const opts = input.options;
23
+ const interval = Math.max(60000, (_a = opts.sweepIntervalMs) !== null && _a !== void 0 ? _a : 24 * 60 * 60 * 1000);
24
+ const dateColumn = input.dateColumn || 'createdAt';
25
+ const label = input.label || input.table;
26
+ if ((!opts.days || opts.days <= 0) && !opts.maxRows) {
27
+ // Nothing to do — caller wants infinite retention.
28
+ return () => undefined;
29
+ }
30
+ let stopped = false;
31
+ let timer = null;
32
+ const sweep = async () => {
33
+ var _a, _b, _c, _d, _e, _f;
34
+ if (stopped)
35
+ return;
36
+ const conn = input.getConnection();
37
+ if (!conn)
38
+ return;
39
+ try {
40
+ if (opts.days && opts.days > 0) {
41
+ const res = await conn.query(`DELETE FROM \`${input.table}\` WHERE \`${dateColumn}\` < DATE_SUB(NOW(), INTERVAL ? DAY)`, [opts.days]);
42
+ const affected = ((_b = (_a = res === null || res === void 0 ? void 0 : res.affectedRows) !== null && _a !== void 0 ? _a : res === null || res === void 0 ? void 0 : res[1]) !== null && _b !== void 0 ? _b : 0);
43
+ if (affected) {
44
+ core_1.Logger.info(`[${label}] pruned ${affected} row(s) older than ${opts.days}d`, loggerCtx);
45
+ }
46
+ }
47
+ if (opts.maxRows && opts.maxRows > 0) {
48
+ const countRows = await conn.query(`SELECT COUNT(*) AS n FROM \`${input.table}\``);
49
+ const total = Number((_d = (_c = countRows === null || countRows === void 0 ? void 0 : countRows[0]) === null || _c === void 0 ? void 0 : _c.n) !== null && _d !== void 0 ? _d : 0);
50
+ const over = total - opts.maxRows;
51
+ if (over > 0) {
52
+ // Delete the `over` oldest rows.
53
+ const res = await conn.query(`DELETE FROM \`${input.table}\` ORDER BY \`${dateColumn}\` ASC LIMIT ?`, [over]);
54
+ const affected = ((_f = (_e = res === null || res === void 0 ? void 0 : res.affectedRows) !== null && _e !== void 0 ? _e : res === null || res === void 0 ? void 0 : res[1]) !== null && _f !== void 0 ? _f : 0);
55
+ core_1.Logger.info(`[${label}] pruned ${affected} oldest row(s) — cap=${opts.maxRows}`, loggerCtx);
56
+ }
57
+ }
58
+ }
59
+ catch (e) {
60
+ core_1.Logger.warn(`[${label}] retention sweep failed: ${e === null || e === void 0 ? void 0 : e.message}`, loggerCtx);
61
+ }
62
+ };
63
+ // First sweep 60s after start (don't slow boot), then on the interval.
64
+ setTimeout(() => { void sweep(); }, 60000);
65
+ timer = setInterval(() => { void sweep(); }, interval);
66
+ if (typeof timer.unref === 'function')
67
+ timer.unref();
68
+ return () => {
69
+ stopped = true;
70
+ if (timer) {
71
+ clearInterval(timer);
72
+ timer = null;
73
+ }
74
+ };
75
+ }
76
+ //# sourceMappingURL=retention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.js","sourceRoot":"","sources":["../src/retention.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Verify an HMAC-SHA256 hex signature against `body` using `secret`.
3
+ *
4
+ * const ok = verifyHmacSha256(req.rawBody, req.header('x-signature'), secret);
5
+ *
6
+ * Constant-time comparison so the signature can't be brute-forced via
7
+ * timing. Returns `false` on any malformed input.
8
+ */
9
+ export declare function verifyHmacSha256(body: string | Buffer, providedSignature: string | undefined | null, secret: string): boolean;
10
+ /**
11
+ * Append a short HMAC tag to a value so we can detect tampering when the
12
+ * value round-trips through a cookie / URL parameter / form field.
13
+ *
14
+ * const signed = signValue('visitor-abc', SECRET);
15
+ * // -> 'visitor-abc.QRwy3...'
16
+ *
17
+ * The tag is 16 hex characters (64 bits) — enough to defeat brute-force
18
+ * for any value we sign here, while keeping cookies short.
19
+ */
20
+ export declare function signValue(value: string, secret: string): string;
21
+ /**
22
+ * Verify the tag and return the original value, or `null` if the tag
23
+ * doesn't match or the format is malformed.
24
+ */
25
+ export declare function verifySignedValue(signed: string | undefined | null, secret: string): string | null;
26
+ export interface RateLimiterOptions {
27
+ /** Allowed events per window. */
28
+ capacity: number;
29
+ /** Window length in milliseconds. */
30
+ windowMs: number;
31
+ /** Maximum tracked keys (LRU-evicted past this). Default 10 000. */
32
+ maxKeys?: number;
33
+ }
34
+ /**
35
+ * Token-bucket rate limiter with an LRU cap on the keyspace so we can't
36
+ * be DoS'd by an attacker pushing keys to grow memory. `allow(key)`
37
+ * returns `true` if the request fits in the bucket. Keys are arbitrary
38
+ * strings — typically `${ip}|${route}`.
39
+ */
40
+ export declare class RateLimiter {
41
+ private readonly capacity;
42
+ private readonly windowMs;
43
+ private readonly maxKeys;
44
+ /** Map<key, [tokensRemaining, lastRefillEpochMs]>. JS Map preserves
45
+ * insertion order, which is good enough LRU for this use. */
46
+ private readonly buckets;
47
+ constructor(opts: RateLimiterOptions);
48
+ allow(key: string, cost?: number): boolean;
49
+ private touchLru;
50
+ /** Reset all buckets — useful in tests. */
51
+ clear(): void;
52
+ }
53
+ /**
54
+ * Apply a recommended security-headers baseline to an Express response.
55
+ *
56
+ * import { applySecurityHeaders } from '@huloglobal/vendure-licence-sdk';
57
+ * applySecurityHeaders(res, { strict: true });
58
+ *
59
+ * `strict` adds a tight Content-Security-Policy suitable for JSON / API
60
+ * endpoints that never embed third-party content. For HTML responses
61
+ * that need styling / scripts, leave `strict` off and set your own CSP.
62
+ */
63
+ export declare function applySecurityHeaders(res: {
64
+ setHeader: (name: string, value: string) => unknown;
65
+ }, opts?: {
66
+ strict?: boolean;
67
+ }): void;
68
+ /**
69
+ * Returns `true` only when `url` parses as an http(s) URL and its
70
+ * hostname matches one of `allowedDomains`. Wildcard suffixes are
71
+ * supported — `*.example.com` matches `foo.example.com` and
72
+ * `bar.foo.example.com` but not `example.com`.
73
+ *
74
+ * Empty `allowedDomains` means "allow any http(s) URL" — the function
75
+ * still rejects `javascript:`, `data:`, `file:` and similar.
76
+ */
77
+ export declare function isUrlOnAllowlist(url: string | undefined | null, allowedDomains: string[]): boolean;
78
+ /**
79
+ * Hash an IP with a per-install salt so we can store it for unique-visitor
80
+ * counts without exposing the raw address. 32-char output (128 bits).
81
+ */
82
+ export declare function hashIp(ip: string | null | undefined, salt: string): string | null;
83
+ /** Generate a URL-safe random token of N bytes (base64url-encoded). */
84
+ export declare function randomToken(bytes?: number): string;
85
+ //# sourceMappingURL=security.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAsBA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC5B,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,iBAAiB,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAC5C,MAAM,EAAE,MAAM,GACf,OAAO,CAcT;AAID;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYlG;AAID,MAAM,WAAW,kBAAkB;IAC/B,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC;kEAC8D;IAC9D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;gBAEnD,IAAI,EAAE,kBAAkB;IAMpC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO;IA4B7C,OAAO,CAAC,QAAQ;IAShB,2CAA2C;IAC3C,KAAK,IAAI,IAAI;CAChB;AAID;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE;IAAE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,EAAE,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,IAAI,CASxI;AAID;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAuBlG;AAID;;;GAGG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjF;AAID,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,KAAK,GAAE,MAAW,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RateLimiter = void 0;
4
+ exports.verifyHmacSha256 = verifyHmacSha256;
5
+ exports.signValue = signValue;
6
+ exports.verifySignedValue = verifySignedValue;
7
+ exports.applySecurityHeaders = applySecurityHeaders;
8
+ exports.isUrlOnAllowlist = isUrlOnAllowlist;
9
+ exports.hashIp = hashIp;
10
+ exports.randomToken = randomToken;
11
+ /**
12
+ * Shared security primitives for HULO Vendure plugins.
13
+ *
14
+ * - HMAC-SHA256 verification (timing-safe) for webhooks
15
+ * - Signed-value helpers (sign / verify) — used to seal cookies,
16
+ * query parameters and any other untrusted-channel value
17
+ * - In-memory token-bucket rate limiter keyed by an arbitrary string
18
+ * - Recommended security-headers helper for Express responses
19
+ * - URL allowlist verifier — defends the click redirector from being
20
+ * used as an open redirector
21
+ * - One-shot IP hashing helper (sha256 with a per-install salt)
22
+ *
23
+ * All helpers are dependency-free (Node built-ins only) and run in
24
+ * sub-millisecond time on a modern CPU. They never throw — errors bubble
25
+ * out as `false` / `null` returns so the calling code never crashes a
26
+ * request on a hostile input.
27
+ */
28
+ const crypto_1 = require("crypto");
29
+ const url_1 = require("url");
30
+ // ── HMAC ────────────────────────────────────────────────────────────────
31
+ /**
32
+ * Verify an HMAC-SHA256 hex signature against `body` using `secret`.
33
+ *
34
+ * const ok = verifyHmacSha256(req.rawBody, req.header('x-signature'), secret);
35
+ *
36
+ * Constant-time comparison so the signature can't be brute-forced via
37
+ * timing. Returns `false` on any malformed input.
38
+ */
39
+ function verifyHmacSha256(body, providedSignature, secret) {
40
+ if (!secret || !providedSignature)
41
+ return false;
42
+ try {
43
+ // Tolerate both bare hex and the `sha256=<hex>` GitHub-style prefix.
44
+ const provided = providedSignature.replace(/^sha256=/, '').trim().toLowerCase();
45
+ if (!/^[a-f0-9]{64}$/.test(provided))
46
+ return false;
47
+ const expected = (0, crypto_1.createHmac)('sha256', secret).update(body).digest('hex');
48
+ const a = Buffer.from(expected, 'utf8');
49
+ const b = Buffer.from(provided, 'utf8');
50
+ if (a.length !== b.length)
51
+ return false;
52
+ return (0, crypto_1.timingSafeEqual)(a, b);
53
+ }
54
+ catch {
55
+ return false;
56
+ }
57
+ }
58
+ // ── Signed values ──────────────────────────────────────────────────────
59
+ /**
60
+ * Append a short HMAC tag to a value so we can detect tampering when the
61
+ * value round-trips through a cookie / URL parameter / form field.
62
+ *
63
+ * const signed = signValue('visitor-abc', SECRET);
64
+ * // -> 'visitor-abc.QRwy3...'
65
+ *
66
+ * The tag is 16 hex characters (64 bits) — enough to defeat brute-force
67
+ * for any value we sign here, while keeping cookies short.
68
+ */
69
+ function signValue(value, secret) {
70
+ const tag = (0, crypto_1.createHmac)('sha256', secret).update(value).digest('hex').slice(0, 16);
71
+ return `${value}.${tag}`;
72
+ }
73
+ /**
74
+ * Verify the tag and return the original value, or `null` if the tag
75
+ * doesn't match or the format is malformed.
76
+ */
77
+ function verifySignedValue(signed, secret) {
78
+ if (!signed || typeof signed !== 'string')
79
+ return null;
80
+ const lastDot = signed.lastIndexOf('.');
81
+ if (lastDot <= 0 || lastDot >= signed.length - 1)
82
+ return null;
83
+ const value = signed.slice(0, lastDot);
84
+ const tag = signed.slice(lastDot + 1).toLowerCase();
85
+ if (!/^[a-f0-9]{16}$/.test(tag))
86
+ return null;
87
+ const expected = (0, crypto_1.createHmac)('sha256', secret).update(value).digest('hex').slice(0, 16);
88
+ const a = Buffer.from(expected, 'utf8');
89
+ const b = Buffer.from(tag, 'utf8');
90
+ if (a.length !== b.length)
91
+ return null;
92
+ return (0, crypto_1.timingSafeEqual)(a, b) ? value : null;
93
+ }
94
+ /**
95
+ * Token-bucket rate limiter with an LRU cap on the keyspace so we can't
96
+ * be DoS'd by an attacker pushing keys to grow memory. `allow(key)`
97
+ * returns `true` if the request fits in the bucket. Keys are arbitrary
98
+ * strings — typically `${ip}|${route}`.
99
+ */
100
+ class RateLimiter {
101
+ constructor(opts) {
102
+ /** Map<key, [tokensRemaining, lastRefillEpochMs]>. JS Map preserves
103
+ * insertion order, which is good enough LRU for this use. */
104
+ this.buckets = new Map();
105
+ this.capacity = Math.max(1, opts.capacity);
106
+ this.windowMs = Math.max(1, opts.windowMs);
107
+ this.maxKeys = Math.max(100, opts.maxKeys || 10000);
108
+ }
109
+ allow(key, cost = 1) {
110
+ if (!key)
111
+ return true;
112
+ const now = Date.now();
113
+ const ratePerMs = this.capacity / this.windowMs;
114
+ let entry = this.buckets.get(key);
115
+ if (entry) {
116
+ const [tokens, last] = entry;
117
+ const refilled = Math.min(this.capacity, tokens + (now - last) * ratePerMs);
118
+ if (refilled < cost) {
119
+ // Update last so timing leaks aren't useful.
120
+ this.buckets.set(key, [refilled, now]);
121
+ this.touchLru(key);
122
+ return false;
123
+ }
124
+ this.buckets.set(key, [refilled - cost, now]);
125
+ this.touchLru(key);
126
+ return true;
127
+ }
128
+ // New key — start with a full bucket minus this request.
129
+ if (this.buckets.size >= this.maxKeys) {
130
+ // Drop the oldest entry (Map.keys() is insertion-ordered).
131
+ const oldest = this.buckets.keys().next().value;
132
+ if (oldest !== undefined)
133
+ this.buckets.delete(oldest);
134
+ }
135
+ this.buckets.set(key, [this.capacity - cost, now]);
136
+ return true;
137
+ }
138
+ touchLru(key) {
139
+ // Move to "most recently used" by re-inserting.
140
+ const entry = this.buckets.get(key);
141
+ if (entry) {
142
+ this.buckets.delete(key);
143
+ this.buckets.set(key, entry);
144
+ }
145
+ }
146
+ /** Reset all buckets — useful in tests. */
147
+ clear() { this.buckets.clear(); }
148
+ }
149
+ exports.RateLimiter = RateLimiter;
150
+ // ── Security headers ───────────────────────────────────────────────────
151
+ /**
152
+ * Apply a recommended security-headers baseline to an Express response.
153
+ *
154
+ * import { applySecurityHeaders } from '@huloglobal/vendure-licence-sdk';
155
+ * applySecurityHeaders(res, { strict: true });
156
+ *
157
+ * `strict` adds a tight Content-Security-Policy suitable for JSON / API
158
+ * endpoints that never embed third-party content. For HTML responses
159
+ * that need styling / scripts, leave `strict` off and set your own CSP.
160
+ */
161
+ function applySecurityHeaders(res, opts = {}) {
162
+ res.setHeader('X-Content-Type-Options', 'nosniff');
163
+ res.setHeader('X-Frame-Options', 'DENY');
164
+ res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
165
+ res.setHeader('Permissions-Policy', 'geolocation=(), camera=(), microphone=(), payment=()');
166
+ res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
167
+ if (opts.strict) {
168
+ res.setHeader('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none'");
169
+ }
170
+ }
171
+ // ── URL allowlist ──────────────────────────────────────────────────────
172
+ /**
173
+ * Returns `true` only when `url` parses as an http(s) URL and its
174
+ * hostname matches one of `allowedDomains`. Wildcard suffixes are
175
+ * supported — `*.example.com` matches `foo.example.com` and
176
+ * `bar.foo.example.com` but not `example.com`.
177
+ *
178
+ * Empty `allowedDomains` means "allow any http(s) URL" — the function
179
+ * still rejects `javascript:`, `data:`, `file:` and similar.
180
+ */
181
+ function isUrlOnAllowlist(url, allowedDomains) {
182
+ if (!url || typeof url !== 'string')
183
+ return false;
184
+ let parsed;
185
+ try {
186
+ parsed = new url_1.URL(url);
187
+ }
188
+ catch {
189
+ return false;
190
+ }
191
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
192
+ return false;
193
+ if (!(allowedDomains === null || allowedDomains === void 0 ? void 0 : allowedDomains.length))
194
+ return true;
195
+ const host = parsed.hostname.toLowerCase();
196
+ for (const allowed of allowedDomains) {
197
+ const norm = allowed.trim().toLowerCase();
198
+ if (!norm)
199
+ continue;
200
+ if (norm.startsWith('*.')) {
201
+ const suffix = norm.slice(2);
202
+ if (host === suffix)
203
+ continue; // *.example.com must NOT match example.com
204
+ if (host.endsWith('.' + suffix))
205
+ return true;
206
+ }
207
+ else if (host === norm) {
208
+ return true;
209
+ }
210
+ }
211
+ return false;
212
+ }
213
+ // ── IP hashing ─────────────────────────────────────────────────────────
214
+ /**
215
+ * Hash an IP with a per-install salt so we can store it for unique-visitor
216
+ * counts without exposing the raw address. 32-char output (128 bits).
217
+ */
218
+ function hashIp(ip, salt) {
219
+ if (!ip)
220
+ return null;
221
+ return (0, crypto_1.createHash)('sha256').update(salt + '|' + ip).digest('hex').slice(0, 32);
222
+ }
223
+ // ── Random ─────────────────────────────────────────────────────────────
224
+ /** Generate a URL-safe random token of N bytes (base64url-encoded). */
225
+ function randomToken(bytes = 32) {
226
+ return (0, crypto_1.randomBytes)(bytes).toString('base64url');
227
+ }
228
+ //# sourceMappingURL=security.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ export interface UpdateStatus {
2
+ packageName: string;
3
+ current: string;
4
+ latest: string | null;
5
+ updateAvailable: boolean;
6
+ /** A "major"-level update is one where the first non-zero version
7
+ * component changes (semver-friendly for 0.x packages). */
8
+ isMajor: boolean;
9
+ lastCheckedAt: Date | null;
10
+ lastError: string | null;
11
+ }
12
+ /**
13
+ * Polls the public npm registry for the latest version of a plugin's
14
+ * package and exposes a small status object. Each plugin instantiates
15
+ * one UpdateChecker at boot and surfaces its status via its `/status`
16
+ * endpoint — the admin UI then shows an "update available" banner.
17
+ *
18
+ * Failures (network, 404) keep the previous cached `latest` value in
19
+ * memory so a brief npm outage doesn't make the dashboard misreport.
20
+ * The cache is wiped only when a fresh value arrives.
21
+ */
22
+ export declare class UpdateChecker {
23
+ private readonly packageName;
24
+ private readonly currentVersion;
25
+ private readonly pollMs;
26
+ private readonly registryUrl;
27
+ private latest;
28
+ private timer;
29
+ private lastCheckedAt;
30
+ private lastError;
31
+ constructor(packageName: string, currentVersion: string, pollMs?: number, // 24h
32
+ registryUrl?: string);
33
+ /** Start the background poll. Idempotent. */
34
+ start(): void;
35
+ stop(): void;
36
+ /** Force a fresh check. Returns the new status. */
37
+ refresh(): Promise<UpdateStatus>;
38
+ getStatus(): UpdateStatus;
39
+ }
40
+ //# sourceMappingURL=update-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB;gEAC4D;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,qBAAa,aAAa;IAOlB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAThC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,SAAS,CAAuB;gBAGnB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,MAAM,GAAE,MAA4B,EAAE,MAAM;IAC5C,WAAW,GAAE,MAAqC;IAGvE,6CAA6C;IAC7C,KAAK,IAAI,IAAI;IAQb,IAAI,IAAI,IAAI;IAOZ,mDAAmD;IAC7C,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC;IAkCtC,SAAS,IAAI,YAAY;CAe5B"}
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UpdateChecker = void 0;
4
+ const core_1 = require("@vendure/core");
5
+ const loggerCtx = 'LicenceSdk:UpdateCheck';
6
+ /**
7
+ * Polls the public npm registry for the latest version of a plugin's
8
+ * package and exposes a small status object. Each plugin instantiates
9
+ * one UpdateChecker at boot and surfaces its status via its `/status`
10
+ * endpoint — the admin UI then shows an "update available" banner.
11
+ *
12
+ * Failures (network, 404) keep the previous cached `latest` value in
13
+ * memory so a brief npm outage doesn't make the dashboard misreport.
14
+ * The cache is wiped only when a fresh value arrives.
15
+ */
16
+ class UpdateChecker {
17
+ constructor(packageName, currentVersion, pollMs = 24 * 60 * 60 * 1000, // 24h
18
+ registryUrl = 'https://registry.npmjs.org') {
19
+ this.packageName = packageName;
20
+ this.currentVersion = currentVersion;
21
+ this.pollMs = pollMs;
22
+ this.registryUrl = registryUrl;
23
+ this.latest = null;
24
+ this.timer = null;
25
+ this.lastCheckedAt = null;
26
+ this.lastError = null;
27
+ }
28
+ /** Start the background poll. Idempotent. */
29
+ start() {
30
+ if (this.timer)
31
+ return;
32
+ // First check runs 30s after boot so we don't slow down startup.
33
+ setTimeout(() => this.refresh().catch(() => undefined), 30000);
34
+ this.timer = setInterval(() => this.refresh().catch(() => undefined), this.pollMs);
35
+ if (typeof this.timer.unref === 'function')
36
+ this.timer.unref();
37
+ }
38
+ stop() {
39
+ if (this.timer) {
40
+ clearInterval(this.timer);
41
+ this.timer = null;
42
+ }
43
+ }
44
+ /** Force a fresh check. Returns the new status. */
45
+ async refresh() {
46
+ const controller = new AbortController();
47
+ const t = setTimeout(() => controller.abort(), 8000);
48
+ try {
49
+ // The `latest` endpoint is small (a few hundred bytes) and
50
+ // cache-friendly — the registry returns a fresh dist-tags
51
+ // entry without the full package metadata payload.
52
+ const res = await fetch(`${this.registryUrl}/${encodeURIComponent(this.packageName)}/latest`, { signal: controller.signal, headers: { accept: 'application/json' } });
53
+ if (!res.ok) {
54
+ this.lastError = `npm registry ${res.status}`;
55
+ core_1.Logger.warn(`Update check for ${this.packageName} failed: ${this.lastError}`, loggerCtx);
56
+ return this.getStatus();
57
+ }
58
+ const body = await res.json();
59
+ const next = String((body === null || body === void 0 ? void 0 : body.version) || '').trim();
60
+ if (!next) {
61
+ this.lastError = 'no version in response';
62
+ return this.getStatus();
63
+ }
64
+ this.latest = next;
65
+ this.lastCheckedAt = new Date();
66
+ this.lastError = null;
67
+ }
68
+ catch (e) {
69
+ this.lastError = (e === null || e === void 0 ? void 0 : e.message) || 'fetch failed';
70
+ core_1.Logger.warn(`Update check for ${this.packageName} failed: ${this.lastError}`, loggerCtx);
71
+ }
72
+ finally {
73
+ clearTimeout(t);
74
+ }
75
+ return this.getStatus();
76
+ }
77
+ getStatus() {
78
+ const updateAvailable = !!this.latest && compareVersions(this.latest, this.currentVersion) > 0;
79
+ const isMajor = updateAvailable
80
+ ? isMajorBump(this.currentVersion, this.latest)
81
+ : false;
82
+ return {
83
+ packageName: this.packageName,
84
+ current: this.currentVersion,
85
+ latest: this.latest,
86
+ updateAvailable,
87
+ isMajor,
88
+ lastCheckedAt: this.lastCheckedAt,
89
+ lastError: this.lastError,
90
+ };
91
+ }
92
+ }
93
+ exports.UpdateChecker = UpdateChecker;
94
+ /**
95
+ * Tiny semver-ish compare. Treats versions as dot-separated number
96
+ * components; suffix tags (`-rc.1`, `-beta`) sort before the release.
97
+ * Sufficient for our case — both sides come from npm dist-tags.
98
+ */
99
+ function compareVersions(a, b) {
100
+ const [ah, at] = splitTag(a);
101
+ const [bh, bt] = splitTag(b);
102
+ const ap = ah.split('.').map(n => parseInt(n, 10) || 0);
103
+ const bp = bh.split('.').map(n => parseInt(n, 10) || 0);
104
+ const len = Math.max(ap.length, bp.length);
105
+ for (let i = 0; i < len; i++) {
106
+ const av = ap[i] || 0;
107
+ const bv = bp[i] || 0;
108
+ if (av !== bv)
109
+ return av - bv;
110
+ }
111
+ // Pre-release sorts BEFORE release (0.2.0-rc.1 < 0.2.0).
112
+ if (at && !bt)
113
+ return -1;
114
+ if (!at && bt)
115
+ return 1;
116
+ if (at && bt)
117
+ return at.localeCompare(bt);
118
+ return 0;
119
+ }
120
+ function splitTag(v) {
121
+ const dash = v.indexOf('-');
122
+ return dash === -1 ? [v, ''] : [v.slice(0, dash), v.slice(dash + 1)];
123
+ }
124
+ /**
125
+ * "Major bump" for the dashboard banner — uses the first non-zero
126
+ * version component as the major identifier (so 0.2.x → 0.3.x is major
127
+ * for 0.x packages, where the user expects breaking changes in 0.y
128
+ * bumps).
129
+ */
130
+ function isMajorBump(current, next) {
131
+ const cp = current.split('.').map(n => parseInt(n, 10) || 0);
132
+ const np = next.split('.').map(n => parseInt(n, 10) || 0);
133
+ // Find the first non-zero index of current.
134
+ let i = 0;
135
+ while (i < cp.length && cp[i] === 0)
136
+ i++;
137
+ return (cp[i] || 0) !== (np[i] || 0);
138
+ }
139
+ //# sourceMappingURL=update-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.js","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huloglobal/vendure-licence-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Licence validation helpers for HULO Vendure plugins. JWT verification, revocation polling and an admin status check used by paid HULO plugins to confirm the host installation is licensed.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Wayne Garrison <wayne@garrison.me.uk>",