@sentriflow/core 0.2.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/README.md CHANGED
@@ -20,6 +20,7 @@ bun add @sentriflow/core
20
20
  - **AST-based parsing**: Converts configurations into a vendor-agnostic Abstract Syntax Tree
21
21
  - **Extensible rule engine**: Define compliance rules for best practices or organization-specific policies
22
22
  - **IP/Subnet Extraction**: Extract and deduplicate IP addresses and CIDR subnets from configurations
23
+ - **GRX2 Loader**: Load and decrypt extended encrypted rule packs for offline usage
23
24
  - **TypeScript native**: Full type safety with comprehensive type definitions
24
25
 
25
26
  ## Supported Vendors
@@ -104,6 +105,71 @@ const summary = extractIPSummary(config);
104
105
  - `includeSubnetNetworks`: Include subnet network addresses in address lists
105
106
  - `skipIPv4`, `skipIPv6`, `skipSubnets`: Skip specific extraction types
106
107
 
108
+ ## GRX2 Loader Module
109
+
110
+ The `grx2-loader` module provides functionality for loading extended encrypted rule packs (.grx2). These packs embed wrapped encryption keys, enabling offline scanning without network access.
111
+
112
+ ### Exported Functions
113
+
114
+ ```typescript
115
+ import {
116
+ loadExtendedPack,
117
+ loadAllPacks,
118
+ getPackInfo,
119
+ getMachineId,
120
+ getMachineIdSync,
121
+ isExtendedGRX2,
122
+ } from '@sentriflow/core/grx2-loader';
123
+ ```
124
+
125
+ | Function | Description |
126
+ |----------|-------------|
127
+ | `loadExtendedPack(filePath, licenseKey, machineId?)` | Load and decrypt a single GRX2 pack |
128
+ | `loadAllPacks(directory, licenseKey, machineId?)` | Load all GRX2 packs from a directory |
129
+ | `getPackInfo(filePath)` | Get metadata from a pack without decrypting |
130
+ | `getMachineId()` | Get the current machine identifier (async) |
131
+ | `getMachineIdSync()` | Get the current machine identifier (sync) |
132
+ | `isExtendedGRX2(buffer)` | Check if a buffer contains an extended GRX2 pack |
133
+
134
+ ### Types
135
+
136
+ ```typescript
137
+ import type {
138
+ GRX2ExtendedHeader,
139
+ GRX2PackLoadResult,
140
+ EncryptedPackInfo,
141
+ EncryptedPackErrorCode,
142
+ LicensePayload,
143
+ } from '@sentriflow/core/grx2-loader';
144
+
145
+ import { EncryptedPackError } from '@sentriflow/core/grx2-loader';
146
+ ```
147
+
148
+ ### Example Usage
149
+
150
+ ```typescript
151
+ import { loadExtendedPack, getMachineId } from '@sentriflow/core/grx2-loader';
152
+
153
+ const licenseKey = process.env.SENTRIFLOW_LICENSE_KEY;
154
+ const machineId = await getMachineId();
155
+
156
+ try {
157
+ const result = await loadExtendedPack('./rules.grx2', licenseKey, machineId);
158
+ if (result.success) {
159
+ console.log(`Loaded ${result.totalRules} rules`);
160
+ }
161
+ } catch (error) {
162
+ if (error instanceof EncryptedPackError) {
163
+ console.error(`Pack error: ${error.code} - ${error.message}`);
164
+ }
165
+ }
166
+ ```
167
+
168
+ ### Machine-Bound vs Portable Packs
169
+
170
+ - **Portable packs**: Pass empty string for `machineId` parameter
171
+ - **Machine-bound packs**: Pass the result of `getMachineId()` for device-specific binding
172
+
107
173
  ## Related Packages
108
174
 
109
175
  - [`@sentriflow/cli`](https://github.com/sentriflow/sentriflow/tree/main/packages/cli) - Command-line interface
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@sentriflow/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "SentriFlow core engine for network configuration validation",
5
5
  "license": "Apache-2.0",
6
6
  "module": "src/index.ts",
7
7
  "type": "module",
8
8
  "exports": {
9
9
  ".": "./src/index.ts",
10
+ "./grx2-loader": "./src/grx2-loader/index.ts",
10
11
  "./helpers": "./src/helpers/index.ts",
11
12
  "./helpers/common": "./src/helpers/common/index.ts",
12
13
  "./helpers/arista": "./src/helpers/arista/index.ts",
@@ -51,8 +52,12 @@
51
52
  "publishConfig": {
52
53
  "access": "public"
53
54
  },
55
+ "dependencies": {
56
+ "node-machine-id": "^1.1.12"
57
+ },
54
58
  "devDependencies": {
55
- "bun-types": "latest"
59
+ "bun-types": "latest",
60
+ "@types/node": "^20.0.0"
56
61
  },
57
62
  "peerDependencies": {
58
63
  "typescript": "^5.0.0"
package/src/constants.ts CHANGED
@@ -23,9 +23,12 @@ export const MAX_TRAVERSAL_DEPTH = 20;
23
23
  /** Allowed extensions for config/rules files */
24
24
  export const ALLOWED_CONFIG_EXTENSIONS = ['.js', '.ts', '.mjs', '.cjs'];
25
25
 
26
- /** SEC-012: Allowed extensions for encrypted rule packs */
26
+ /** SEC-012: Allowed extensions for encrypted rule packs (.grpx legacy format) */
27
27
  export const ALLOWED_ENCRYPTED_PACK_EXTENSIONS = ['.grpx'];
28
28
 
29
+ /** Allowed extensions for GRX2 extended encrypted rule packs */
30
+ export const ALLOWED_GRX2_PACK_EXTENSIONS = ['.grx2'];
31
+
29
32
  /** Allowed extensions for JSON rule files */
30
33
  export const ALLOWED_JSON_RULES_EXTENSIONS = ['.json'];
31
34
 
@@ -16,6 +16,8 @@ export interface ExecutionOptions {
16
16
  onTimeout?: (ruleId: string, nodeId: string, elapsedMs: number) => void;
17
17
  /** Callback when a rule is auto-disabled */
18
18
  onRuleDisabled?: (ruleId: string, reason: string) => void;
19
+ /** Callback when a rule throws an error during execution */
20
+ onError?: (ruleId: string, nodeId: string, error: unknown) => void;
19
21
  }
20
22
 
21
23
  /**
@@ -55,7 +57,7 @@ export class RuleExecutor {
55
57
  private timeoutCounts = new Map<string, number>();
56
58
  private executionTimes = new Map<string, { count: number; totalMs: number }>();
57
59
  private disabledRules = new Set<string>();
58
- private options: Required<ExecutionOptions>;
60
+ private options: Required<Omit<ExecutionOptions, 'onError'>> & Pick<ExecutionOptions, 'onError'>;
59
61
 
60
62
  constructor(options: ExecutionOptions = {}) {
61
63
  this.options = {
@@ -63,6 +65,7 @@ export class RuleExecutor {
63
65
  maxTimeouts: options.maxTimeouts ?? 3,
64
66
  onTimeout: options.onTimeout ?? (() => {}),
65
67
  onRuleDisabled: options.onRuleDisabled ?? (() => {}),
68
+ onError: options.onError,
66
69
  };
67
70
  }
68
71
 
@@ -103,6 +106,9 @@ export class RuleExecutor {
103
106
  const elapsed = performance.now() - startTime;
104
107
  this.trackExecutionTime(rule.id, elapsed);
105
108
 
109
+ // Call error callback if provided (for debugging)
110
+ this.options.onError?.(rule.id, node.id, error);
111
+
106
112
  // SEC-005: Sanitize error message to prevent information disclosure
107
113
  // Don't expose internal error details which could reveal file paths or sensitive data
108
114
  return {