sekant-intercept-js 1.0.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/LICENSE +231 -0
- package/NOTICE +15 -0
- package/README.md +327 -0
- package/dist/sekant-intercept.browser.mjs +15 -0
- package/dist/sekant-intercept.node.mjs +15 -0
- package/package.json +69 -0
- package/src/ahocorasickEngine.mjs +229 -0
- package/src/elfModule.mjs +306 -0
- package/src/hashModule.mjs +284 -0
- package/src/index.js +9 -0
- package/src/interceptCustomModules.mjs +230 -0
- package/src/interceptScanner.mjs +865 -0
- package/src/mathModule.mjs +506 -0
- package/src/peModule.mjs +295 -0
- package/src/performanceInstrumentation.mjs +172 -0
- package/src/sharedUtils.mjs +127 -0
- package/src/stringModule.mjs +43 -0
- package/src/timeModule.mjs +19 -0
- package/src/yaraConditionParser.mjs +1083 -0
- package/src/yaraConditionsMatch.mjs +1373 -0
- package/src/yaraRuleCompiler.mjs +448 -0
- package/src/yaraStringMatch.mjs +449 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Rishi Kant (Sekant Security Inc.)
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Sekant Intercept Custom Modules Interface
|
|
19
|
+
*
|
|
20
|
+
* This file defines the interface for custom modules that can be registered
|
|
21
|
+
* with the InterceptScanner. Custom modules extend functionality beyond the
|
|
22
|
+
* standard modules (pe, elf, math, hash, string, time).
|
|
23
|
+
*
|
|
24
|
+
* ============================================================================
|
|
25
|
+
* MODULE LIFECYCLE
|
|
26
|
+
* ============================================================================
|
|
27
|
+
*
|
|
28
|
+
* 1. load()
|
|
29
|
+
* - Called once when module needs initialization (lazy loading during first scan)
|
|
30
|
+
* - Use for: fetching databases, compiling patterns, loading resources
|
|
31
|
+
* - Must catch errors internally and set loaded flag to false on failure
|
|
32
|
+
*
|
|
33
|
+
* 2. initialize(data, metadata)
|
|
34
|
+
* - Called per scan to process the file data
|
|
35
|
+
* - Receives: Uint8Array data and metadata object
|
|
36
|
+
* - Returns: Interim results object (any structure you need)
|
|
37
|
+
* - Must return {} if module not loaded
|
|
38
|
+
*
|
|
39
|
+
* 3. createModule(interimResults)
|
|
40
|
+
* - Called per scan to create final module object for condition evaluation
|
|
41
|
+
* - Receives: Results from initialize()
|
|
42
|
+
* - Returns: Object with properties/functions accessible in YARA conditions
|
|
43
|
+
* - Must return {} if module not loaded
|
|
44
|
+
*
|
|
45
|
+
* ============================================================================
|
|
46
|
+
* REQUIRED INTERFACE
|
|
47
|
+
* ============================================================================
|
|
48
|
+
*
|
|
49
|
+
* getName(): string
|
|
50
|
+
* - Returns module keyword used in YARA conditions (e.g., "mymodule")
|
|
51
|
+
* - Must be lowercase alphanumeric for consistency
|
|
52
|
+
* - Cannot conflict with reserved names (see RESERVED_MODULE_NAMES)
|
|
53
|
+
*
|
|
54
|
+
* isLoaded(): boolean
|
|
55
|
+
* - Returns whether load() completed successfully
|
|
56
|
+
* - Used by scanner to determine if load() needs to be called
|
|
57
|
+
*
|
|
58
|
+
* load(): Promise<void>
|
|
59
|
+
* - One-time initialization
|
|
60
|
+
* - Should catch errors and set internal loaded flag to false
|
|
61
|
+
* - Called lazily on first scan if isLoaded() returns false
|
|
62
|
+
*
|
|
63
|
+
* initialize(data, metadata): Promise<Object>
|
|
64
|
+
* - Process file data and return interim results
|
|
65
|
+
* - data: Uint8Array of file being scanned
|
|
66
|
+
* - metadata: {filesize, hasPE, hasELF, timestamp, ...}
|
|
67
|
+
* - Must return {} if not loaded
|
|
68
|
+
*
|
|
69
|
+
* createModule(interimResults): Object
|
|
70
|
+
* - Create final module object from interim results
|
|
71
|
+
* - Returns object with properties/functions for conditions
|
|
72
|
+
* - Must return {} if not loaded
|
|
73
|
+
*
|
|
74
|
+
* ============================================================================
|
|
75
|
+
* USAGE IN YARA RULES
|
|
76
|
+
* ============================================================================
|
|
77
|
+
*
|
|
78
|
+
* rule ExampleRule {
|
|
79
|
+
* condition:
|
|
80
|
+
* mymodule.property == value and
|
|
81
|
+
* mymodule.method(arg1, arg2)
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* ============================================================================
|
|
85
|
+
* ERROR HANDLING
|
|
86
|
+
* ============================================================================
|
|
87
|
+
*
|
|
88
|
+
* - load() failures: Catch internally, set loaded=false, log warning
|
|
89
|
+
* - initialize() failures: Return {}, log warning
|
|
90
|
+
* - createModule() failures: Return {}
|
|
91
|
+
* - Graceful degradation: Conditions using unavailable modules → undefined/false
|
|
92
|
+
*
|
|
93
|
+
* ============================================================================
|
|
94
|
+
* MODULE NAME RESTRICTIONS
|
|
95
|
+
* ============================================================================
|
|
96
|
+
*
|
|
97
|
+
* Cannot use reserved keywords (case-insensitive):
|
|
98
|
+
* pe, elf, math, hash, string, time, filesize, entrypoint
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Base Custom Module Class
|
|
103
|
+
*
|
|
104
|
+
* Extend this class to create your custom module. Override all methods.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* class MyModule extends BaseCustomModule {
|
|
108
|
+
* constructor() {
|
|
109
|
+
* super();
|
|
110
|
+
* this._loaded = false;
|
|
111
|
+
* }
|
|
112
|
+
*
|
|
113
|
+
* getName() {
|
|
114
|
+
* return 'mymodule';
|
|
115
|
+
* }
|
|
116
|
+
*
|
|
117
|
+
* isLoaded() {
|
|
118
|
+
* return this._loaded;
|
|
119
|
+
* }
|
|
120
|
+
*
|
|
121
|
+
* async load() {
|
|
122
|
+
* try {
|
|
123
|
+
* // Your initialization logic
|
|
124
|
+
* this._loaded = true;
|
|
125
|
+
* } catch (error) {
|
|
126
|
+
* console.warn(`MyModule: Load failed: ${error.message}`);
|
|
127
|
+
* this._loaded = false;
|
|
128
|
+
* }
|
|
129
|
+
* }
|
|
130
|
+
*
|
|
131
|
+
* async initialize(data, metadata) {
|
|
132
|
+
* if (!this._loaded) return {};
|
|
133
|
+
* // Process data and return results
|
|
134
|
+
* return { result: 'value' };
|
|
135
|
+
* }
|
|
136
|
+
*
|
|
137
|
+
* createModule(interimResults) {
|
|
138
|
+
* if (!this._loaded) return {};
|
|
139
|
+
* return {
|
|
140
|
+
* property: interimResults.result,
|
|
141
|
+
* method: (arg) => arg * 2,
|
|
142
|
+
* };
|
|
143
|
+
* }
|
|
144
|
+
* }
|
|
145
|
+
*/
|
|
146
|
+
export class BaseCustomModule {
|
|
147
|
+
|
|
148
|
+
constructor(name) {
|
|
149
|
+
// Initialization logic if needed
|
|
150
|
+
this._isLoaded = false;
|
|
151
|
+
this._name = name;
|
|
152
|
+
if (!isValidModuleName(name)) {
|
|
153
|
+
throw new Error(`Invalid module name: "${name}". It is either reserved or malformed.`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Returns the module keyword for YARA conditions
|
|
159
|
+
* @returns {string} Module name (must be unique and not reserved)
|
|
160
|
+
*/
|
|
161
|
+
getName() {
|
|
162
|
+
return this._name;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Returns whether the module loaded successfully
|
|
167
|
+
* @returns {boolean} Load status
|
|
168
|
+
*/
|
|
169
|
+
isLoaded() {
|
|
170
|
+
return this._isLoaded;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* One-time initialization
|
|
175
|
+
* Should catch errors internally and set loaded flag
|
|
176
|
+
* @returns {Promise<boolean>} Load success status
|
|
177
|
+
*/
|
|
178
|
+
async load() {
|
|
179
|
+
this._isLoaded = true;
|
|
180
|
+
return this._isLoaded;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Process file data per scan
|
|
185
|
+
* @param {Uint8Array} data - File data being scanned
|
|
186
|
+
* @param {Object} metadata - Scan metadata (filesize, hasPE, hasELF, timestamp)
|
|
187
|
+
* @returns {Promise<Object>} Interim results (any structure)
|
|
188
|
+
*/
|
|
189
|
+
async initialize(data, metadata) { // eslint-disable-line no-unused-vars
|
|
190
|
+
throw new Error('CustomModule.initialize() must be implemented');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Create final module object for condition evaluation
|
|
195
|
+
* @param {Object} interimResults - Results from initialize()
|
|
196
|
+
* @returns {Object} Module object with properties and/or functions
|
|
197
|
+
*/
|
|
198
|
+
createModule(interimResults) { // eslint-disable-line no-unused-vars
|
|
199
|
+
throw new Error('CustomModule.createModule() must be implemented');
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Reserved Module Keywords (case-insensitive)
|
|
205
|
+
* These names cannot be used for custom modules
|
|
206
|
+
*/
|
|
207
|
+
export const RESERVED_MODULE_NAMES = [
|
|
208
|
+
'pe',
|
|
209
|
+
'elf',
|
|
210
|
+
'math',
|
|
211
|
+
'hash',
|
|
212
|
+
'string',
|
|
213
|
+
'time',
|
|
214
|
+
'filesize',
|
|
215
|
+
'entrypoint',
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Validate custom module name against reserved keywords
|
|
220
|
+
* @param {string} name - Module name to validate
|
|
221
|
+
* @returns {boolean} True if name is valid (not reserved)
|
|
222
|
+
*/
|
|
223
|
+
export function isValidModuleName(name) {
|
|
224
|
+
if (!name || typeof name !== 'string') {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const nameLower = name.toLowerCase();
|
|
229
|
+
return !RESERVED_MODULE_NAMES.includes(nameLower);
|
|
230
|
+
}
|