net-snmp 3.21.1 → 3.22.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
@@ -2493,6 +2493,27 @@ pre-loaded "base" modules is:
2493
2493
  * SNMPv2-TC
2494
2494
  * SNMPv2-MIB
2495
2495
 
2496
+ By default, the `createModuleStore()` function creates a new `ModuleStore` instance with all the base modules pre-loaded.
2497
+ However, you can now customize which base modules are loaded by passing an options object:
2498
+
2499
+ ```js
2500
+ // Example of selecting only SNMPv2 MIBs
2501
+ const store = snmp.createModuleStore({
2502
+ baseModules: [
2503
+ 'SNMPv2-SMI',
2504
+ 'SNMPv2-CONF',
2505
+ 'SNMPv2-TC',
2506
+ 'SNMPv2-MIB',
2507
+ ],
2508
+ });
2509
+ ```
2510
+
2511
+ The `options` object can contain:
2512
+
2513
+ * `baseModules` - An array of module names to use as the base modules. This allows you to explicitly control which MIBs are loaded, which can be useful to avoid unexpected type overrides that might occur with the full set of base modules.
2514
+
2515
+ This feature is helpful when dealing with constraints for SNMPv2-TC defined textual conventions like DisplayString that might get preempted by subsequent definitions as plain OCTET STRING in RFC MIBs.
2516
+
2496
2517
  ## store.loadFromFile (fileName)
2497
2518
 
2498
2519
  Loads all MIB modules in the given file into the module store. By convention, there is
@@ -3525,6 +3546,16 @@ Example programs are included under the module's `example` directory.
3525
3546
 
3526
3547
  * Add better defval type handling, improved debug handling and simple agent example
3527
3548
 
3549
+ # Version 3.21.2 - 27/04/2025
3550
+
3551
+ * Add custom base module list
3552
+
3553
+ # Version 3.22.0 - 27/04/2025
3554
+
3555
+ * Fix incorrect SNMPv3 engineID handling
3556
+
3557
+ * Add custom base module list
3558
+
3528
3559
  # License
3529
3560
 
3530
3561
  Copyright (c) 2020 Mark Abrahams <mark@abrahams.co.nz>
package/index.js CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  // Copyright 2013 Stephen Vickers <stephen.vickers.sv@gmail.com>
3
2
 
4
3
  const ber = require ("asn1-ber").Ber;
@@ -2038,7 +2037,7 @@ var Session = function (target, authenticator, options) {
2038
2037
  DEBUG |= options.debug;
2039
2038
 
2040
2039
  this.engine = new Engine ({
2041
- engineId: options.engineID
2040
+ engineID: options.engineID
2042
2041
  });
2043
2042
  this.reqs = {};
2044
2043
  this.reqCount = 0;
@@ -3009,7 +3008,7 @@ Session.createV3 = function (target, user, options) {
3009
3008
  };
3010
3009
 
3011
3010
  var Engine = function (engineOptions) {
3012
- const { engineID } = engineOptions;
3011
+ let { engineID } = engineOptions;
3013
3012
  if ( engineID ) {
3014
3013
  if ( ! (engineID instanceof Buffer) ) {
3015
3014
  engineID = engineID.replace('0x', '');
@@ -3355,7 +3354,7 @@ var Receiver = function (options, callback) {
3355
3354
  DEBUG |= options.debug;
3356
3355
  this.authorizer = new Authorizer (options);
3357
3356
  this.engine = new Engine ({
3358
- engineId: options.engineID
3357
+ engineID: options.engineID
3359
3358
  });
3360
3359
 
3361
3360
  this.engineBoots = 0;
@@ -3455,7 +3454,8 @@ Receiver.create = function (options, callback) {
3455
3454
  return receiver;
3456
3455
  };
3457
3456
 
3458
- var ModuleStore = function () {
3457
+ var ModuleStore = function (baseModules) {
3458
+ this.baseModules = baseModules ?? ModuleStore.BASE_MODULES;
3459
3459
  this.parser = mibparser ();
3460
3460
  this.translations = {
3461
3461
  oidToPath: {},
@@ -3545,7 +3545,7 @@ ModuleStore.prototype.getModule = function (moduleName) {
3545
3545
  ModuleStore.prototype.getModules = function (includeBase) {
3546
3546
  var modules = {};
3547
3547
  for ( var moduleName of Object.keys(this.parser.Modules) ) {
3548
- if ( includeBase || ModuleStore.BASE_MODULES.indexOf (moduleName) == -1 ) {
3548
+ if ( includeBase || this.baseModules.indexOf (moduleName) == -1 ) {
3549
3549
  modules[moduleName] = this.parser.Modules[moduleName];
3550
3550
  }
3551
3551
  }
@@ -3555,7 +3555,7 @@ ModuleStore.prototype.getModules = function (includeBase) {
3555
3555
  ModuleStore.prototype.getModuleNames = function (includeBase) {
3556
3556
  var modules = [];
3557
3557
  for ( var moduleName of Object.keys(this.parser.Modules) ) {
3558
- if ( includeBase || ModuleStore.BASE_MODULES.indexOf (moduleName) == -1 ) {
3558
+ if ( includeBase || this.baseModules.indexOf (moduleName) == -1 ) {
3559
3559
  modules.push (moduleName);
3560
3560
  }
3561
3561
  }
@@ -3729,7 +3729,7 @@ ModuleStore.prototype.getProvidersForModule = function (moduleName) {
3729
3729
  };
3730
3730
 
3731
3731
  ModuleStore.prototype.loadBaseModules = function () {
3732
- for ( var mibModule of ModuleStore.BASE_MODULES ) {
3732
+ for ( var mibModule of this.baseModules ) {
3733
3733
  this.parser.Import (__dirname + "/lib/mibs/" + mibModule + ".mib");
3734
3734
  }
3735
3735
  this.parser.Serialize ();
@@ -3813,8 +3813,8 @@ ModuleStore.prototype.translate = function (name, destinationFormat) {
3813
3813
  }
3814
3814
  };
3815
3815
 
3816
- ModuleStore.create = function () {
3817
- var store = new ModuleStore ();
3816
+ ModuleStore.create = function (options) {
3817
+ const store = new ModuleStore (options?.baseModules ?? ModuleStore.BASE_MODULES);
3818
3818
  store.loadBaseModules ();
3819
3819
  return store;
3820
3820
  };
@@ -4873,7 +4873,7 @@ var Agent = function (options, callback, mib) {
4873
4873
  DEBUG |= options.debug;
4874
4874
  this.listener = new Listener (options, this);
4875
4875
  this.engine = new Engine ({
4876
- engineId: options.engineID
4876
+ engineID: options.engineID
4877
4877
  });
4878
4878
  this.authorizer = new Authorizer (options);
4879
4879
  this.callback = callback || function () {};
package/package.json CHANGED
@@ -1,19 +1,16 @@
1
1
  {
2
2
  "name": "net-snmp",
3
- "version": "3.21.1",
3
+ "version": "3.22.0",
4
4
  "description": "JavaScript implementation of the Simple Network Management Protocol (SNMP)",
5
+ "author": "Mark Abrahams <mark@abrahams.co.nz>",
6
+ "license": "MIT",
5
7
  "main": "index.js",
6
8
  "directories": {
7
9
  "example": "example"
8
10
  },
9
- "dependencies": {
10
- "asn1-ber": "^1.2.1",
11
- "smart-buffer": "^4.1.0"
12
- },
13
- "devDependencies": {
14
- "eslint": "^9.9.1",
15
- "getopts": "^2.3.0",
16
- "mocha": "^11.0.1"
11
+ "scripts": {
12
+ "lint": "eslint . ./**/*.js",
13
+ "test": "node --openssl-legacy-provider ./node_modules/mocha/bin/mocha.js"
17
14
  },
18
15
  "contributors": [
19
16
  {
@@ -45,10 +42,13 @@
45
42
  "monitor",
46
43
  "monitoring"
47
44
  ],
48
- "author": "Mark Abrahams <mark@abrahams.co.nz>",
49
- "license": "MIT",
50
- "scripts": {
51
- "lint": "eslint . ./**/*.js",
52
- "test": "./node_modules/mocha/bin/mocha.js"
45
+ "dependencies": {
46
+ "asn1-ber": "^1.2.1",
47
+ "smart-buffer": "^4.1.0"
48
+ },
49
+ "devDependencies": {
50
+ "eslint": "^9.9.1",
51
+ "getopts": "^2.3.0",
52
+ "mocha": "^11.0.1"
53
53
  }
54
54
  }
@@ -0,0 +1,384 @@
1
+ const assert = require('assert');
2
+ // crypto is used indirectly by Authentication and Encryption modules
3
+ const snmp = require('../');
4
+ const { SecurityLevel, AuthProtocols, PrivProtocols, Authentication, Encryption } = snmp;
5
+
6
+ describe('SNMPv3 Authentication and Encryption', function () {
7
+ // Sample data for tests
8
+ const authPassword = 'test_auth_password';
9
+ const privPassword = 'test_priv_password';
10
+ const engineID = Buffer.from('8000B98380ABCDEF', 'hex');
11
+
12
+ // Test data that will be encrypted/authenticated
13
+ const testData = Buffer.from('Test data for SNMP authentication and privacy tests', 'utf8');
14
+
15
+ describe('Authentication', function () {
16
+ it('should support noAuthNoPriv security level', function () {
17
+ // noAuthNoPriv doesn't use authentication, verify this is handled appropriately
18
+ const user = {
19
+ name: 'noAuthUser',
20
+ level: SecurityLevel.noAuthNoPriv,
21
+ };
22
+
23
+ // No authentication should be required for this user
24
+ assert.strictEqual(user.level, SecurityLevel.noAuthNoPriv);
25
+ assert.strictEqual(user.authProtocol, undefined);
26
+ assert.strictEqual(user.authKey, undefined);
27
+ });
28
+
29
+ it('should support MD5 authentication protocol', function () {
30
+ // Generate authentication key using MD5
31
+ const authKey = Authentication.passwordToKey(AuthProtocols.md5, authPassword, engineID);
32
+
33
+ // Verify key length matches expected MD5 output length
34
+ assert.strictEqual(authKey.length, Authentication.algorithms[AuthProtocols.md5].KEY_LENGTH);
35
+
36
+ // Create a test digest
37
+ const digest = Authentication.calculateDigest(testData, AuthProtocols.md5, authPassword, engineID);
38
+
39
+ // Verify digest length
40
+ assert.strictEqual(digest.length, Authentication.algorithms[AuthProtocols.md5].AUTHENTICATION_CODE_LENGTH);
41
+
42
+ // Verify authentication works
43
+ const testBuffer = Buffer.concat([testData]);
44
+ const digestInMessage = Buffer.alloc(
45
+ Authentication.algorithms[AuthProtocols.md5].AUTHENTICATION_CODE_LENGTH
46
+ );
47
+ Authentication.writeParameters(testBuffer, AuthProtocols.md5, authPassword, engineID, digestInMessage);
48
+
49
+ assert.strictEqual(
50
+ Authentication.isAuthentic(testBuffer, AuthProtocols.md5, authPassword, engineID, digestInMessage),
51
+ true
52
+ );
53
+ });
54
+
55
+ it('should support SHA authentication protocol', function () {
56
+ // Generate authentication key using SHA
57
+ const authKey = Authentication.passwordToKey(AuthProtocols.sha, authPassword, engineID);
58
+
59
+ // Verify key length matches expected SHA output length
60
+ assert.strictEqual(authKey.length, Authentication.algorithms[AuthProtocols.sha].KEY_LENGTH);
61
+
62
+ // Create a test digest
63
+ const digest = Authentication.calculateDigest(testData, AuthProtocols.sha, authPassword, engineID);
64
+
65
+ // Verify digest length
66
+ assert.strictEqual(digest.length, Authentication.algorithms[AuthProtocols.sha].AUTHENTICATION_CODE_LENGTH);
67
+
68
+ // Verify authentication works
69
+ const testBuffer = Buffer.concat([testData]);
70
+ const digestInMessage = Buffer.alloc(
71
+ Authentication.algorithms[AuthProtocols.sha].AUTHENTICATION_CODE_LENGTH
72
+ );
73
+ Authentication.writeParameters(testBuffer, AuthProtocols.sha, authPassword, engineID, digestInMessage);
74
+
75
+ assert.strictEqual(
76
+ Authentication.isAuthentic(testBuffer, AuthProtocols.sha, authPassword, engineID, digestInMessage),
77
+ true
78
+ );
79
+ });
80
+
81
+ it('should support SHA-256 authentication protocol', function () {
82
+ // Generate authentication key using SHA-256
83
+ const authKey = Authentication.passwordToKey(AuthProtocols.sha256, authPassword, engineID);
84
+
85
+ // Verify key length matches expected SHA-256 output length
86
+ assert.strictEqual(authKey.length, Authentication.algorithms[AuthProtocols.sha256].KEY_LENGTH);
87
+
88
+ // Create a test digest
89
+ const digest = Authentication.calculateDigest(testData, AuthProtocols.sha256, authPassword, engineID);
90
+
91
+ // Verify digest length
92
+ assert.strictEqual(
93
+ digest.length,
94
+ Authentication.algorithms[AuthProtocols.sha256].AUTHENTICATION_CODE_LENGTH
95
+ );
96
+
97
+ // Verify authentication works
98
+ const testBuffer = Buffer.concat([testData]);
99
+ const digestInMessage = Buffer.alloc(
100
+ Authentication.algorithms[AuthProtocols.sha256].AUTHENTICATION_CODE_LENGTH
101
+ );
102
+ Authentication.writeParameters(testBuffer, AuthProtocols.sha256, authPassword, engineID, digestInMessage);
103
+
104
+ assert.strictEqual(
105
+ Authentication.isAuthentic(testBuffer, AuthProtocols.sha256, authPassword, engineID, digestInMessage),
106
+ true
107
+ );
108
+ });
109
+
110
+ it('should support SHA-512 authentication protocol', function () {
111
+ // Generate authentication key using SHA-512
112
+ const authKey = Authentication.passwordToKey(AuthProtocols.sha512, authPassword, engineID);
113
+
114
+ // Verify key length matches expected SHA-512 output length
115
+ assert.strictEqual(authKey.length, Authentication.algorithms[AuthProtocols.sha512].KEY_LENGTH);
116
+
117
+ // Create a test digest
118
+ const digest = Authentication.calculateDigest(testData, AuthProtocols.sha512, authPassword, engineID);
119
+
120
+ // Verify digest length
121
+ assert.strictEqual(
122
+ digest.length,
123
+ Authentication.algorithms[AuthProtocols.sha512].AUTHENTICATION_CODE_LENGTH
124
+ );
125
+
126
+ // Verify authentication works
127
+ const testBuffer = Buffer.concat([testData]);
128
+ const digestInMessage = Buffer.alloc(
129
+ Authentication.algorithms[AuthProtocols.sha512].AUTHENTICATION_CODE_LENGTH
130
+ );
131
+ Authentication.writeParameters(testBuffer, AuthProtocols.sha512, authPassword, engineID, digestInMessage);
132
+
133
+ assert.strictEqual(
134
+ Authentication.isAuthentic(testBuffer, AuthProtocols.sha512, authPassword, engineID, digestInMessage),
135
+ true
136
+ );
137
+ });
138
+ });
139
+
140
+ describe('Encryption', function () {
141
+ // Create a mock engine for encryption tests
142
+ const engine = {
143
+ engineID: engineID,
144
+ engineBoots: 1,
145
+ engineTime: 123,
146
+ };
147
+
148
+ it('should support DES encryption protocol', function () {
149
+ // Test DES encryption/decryption with SHA authentication
150
+ const authProtocol = AuthProtocols.sha;
151
+ const privProtocol = PrivProtocols.des;
152
+
153
+ // Encrypt the test data
154
+ const { encryptedPdu, msgPrivacyParameters } = Encryption.encryptPdu(
155
+ privProtocol,
156
+ testData,
157
+ privPassword,
158
+ authProtocol,
159
+ engine
160
+ );
161
+
162
+ // Verify encryption was done (output should be different from input)
163
+ assert.notDeepStrictEqual(encryptedPdu, testData);
164
+
165
+ // Decrypt the data
166
+ const decryptedPdu = Encryption.decryptPdu(
167
+ privProtocol,
168
+ encryptedPdu,
169
+ msgPrivacyParameters,
170
+ privPassword,
171
+ authProtocol,
172
+ engine
173
+ );
174
+
175
+ // Verify decryption works
176
+ assert.deepStrictEqual(decryptedPdu.slice(0, testData.length), testData);
177
+ });
178
+
179
+ it('should support AES encryption protocol', function () {
180
+ // Test AES encryption/decryption with SHA authentication
181
+ const authProtocol = AuthProtocols.sha;
182
+ const privProtocol = PrivProtocols.aes;
183
+
184
+ // Encrypt the test data
185
+ const { encryptedPdu, msgPrivacyParameters } = Encryption.encryptPdu(
186
+ privProtocol,
187
+ testData,
188
+ privPassword,
189
+ authProtocol,
190
+ engine
191
+ );
192
+
193
+ // Verify encryption was done (output should be different from input)
194
+ assert.notDeepStrictEqual(encryptedPdu, testData);
195
+
196
+ // Decrypt the data
197
+ const decryptedPdu = Encryption.decryptPdu(
198
+ privProtocol,
199
+ encryptedPdu,
200
+ msgPrivacyParameters,
201
+ privPassword,
202
+ authProtocol,
203
+ engine
204
+ );
205
+
206
+ // Verify decryption works
207
+ assert.deepStrictEqual(decryptedPdu.slice(0, testData.length), testData);
208
+ });
209
+
210
+ it('should support AES-256b (Blumenthal) encryption protocol', function () {
211
+ // Test AES-256 Blumenthal encryption/decryption with SHA authentication
212
+ const authProtocol = AuthProtocols.sha;
213
+ const privProtocol = PrivProtocols.aes256b;
214
+
215
+ // Encrypt the test data
216
+ const { encryptedPdu, msgPrivacyParameters } = Encryption.encryptPdu(
217
+ privProtocol,
218
+ testData,
219
+ privPassword,
220
+ authProtocol,
221
+ engine
222
+ );
223
+
224
+ // Verify encryption was done (output should be different from input)
225
+ assert.notDeepStrictEqual(encryptedPdu, testData);
226
+
227
+ // Decrypt the data
228
+ const decryptedPdu = Encryption.decryptPdu(
229
+ privProtocol,
230
+ encryptedPdu,
231
+ msgPrivacyParameters,
232
+ privPassword,
233
+ authProtocol,
234
+ engine
235
+ );
236
+
237
+ // Verify decryption works
238
+ assert.deepStrictEqual(decryptedPdu.slice(0, testData.length), testData);
239
+ });
240
+
241
+ it('should support AES-256r (Reeder) encryption protocol', function () {
242
+ // Test AES-256 Reeder encryption/decryption with SHA authentication
243
+ const authProtocol = AuthProtocols.sha;
244
+ const privProtocol = PrivProtocols.aes256r;
245
+
246
+ // Encrypt the test data
247
+ const { encryptedPdu, msgPrivacyParameters } = Encryption.encryptPdu(
248
+ privProtocol,
249
+ testData,
250
+ privPassword,
251
+ authProtocol,
252
+ engine
253
+ );
254
+
255
+ // Verify encryption was done (output should be different from input)
256
+ assert.notDeepStrictEqual(encryptedPdu, testData);
257
+
258
+ // Decrypt the data
259
+ const decryptedPdu = Encryption.decryptPdu(
260
+ privProtocol,
261
+ encryptedPdu,
262
+ msgPrivacyParameters,
263
+ privPassword,
264
+ authProtocol,
265
+ engine
266
+ );
267
+
268
+ // Verify decryption works
269
+ assert.deepStrictEqual(decryptedPdu.slice(0, testData.length), testData);
270
+ });
271
+ });
272
+
273
+ describe('SecurityLevel combinations', function () {
274
+ it('should support authNoPriv security level', function () {
275
+ // Create a user with authentication but no privacy
276
+ const user = {
277
+ name: 'authNoPrivUser',
278
+ level: SecurityLevel.authNoPriv,
279
+ authProtocol: AuthProtocols.sha,
280
+ authKey: 'authPassword',
281
+ };
282
+
283
+ assert.strictEqual(user.level, SecurityLevel.authNoPriv);
284
+ assert.strictEqual(user.authProtocol, AuthProtocols.sha);
285
+ assert.strictEqual(user.authKey, 'authPassword');
286
+ assert.strictEqual(user.privProtocol, undefined);
287
+ assert.strictEqual(user.privKey, undefined);
288
+ });
289
+
290
+ it('should support authPriv security level', function () {
291
+ // Create a user with authentication and privacy
292
+ const user = {
293
+ name: 'authPrivUser',
294
+ level: SecurityLevel.authPriv,
295
+ authProtocol: AuthProtocols.sha256,
296
+ authKey: 'authPassword',
297
+ privProtocol: PrivProtocols.aes,
298
+ privKey: 'privPassword',
299
+ };
300
+
301
+ assert.strictEqual(user.level, SecurityLevel.authPriv);
302
+ assert.strictEqual(user.authProtocol, AuthProtocols.sha256);
303
+ assert.strictEqual(user.authKey, 'authPassword');
304
+ assert.strictEqual(user.privProtocol, PrivProtocols.aes);
305
+ assert.strictEqual(user.privKey, 'privPassword');
306
+ });
307
+
308
+ it('should validate all required parameters are provided for each security level', function () {
309
+ // noAuthNoPriv only requires username and level
310
+ const user1 = {
311
+ name: 'user1',
312
+ level: SecurityLevel.noAuthNoPriv,
313
+ };
314
+
315
+ // authNoPriv requires authentication parameters
316
+ const user2 = {
317
+ name: 'user2',
318
+ level: SecurityLevel.authNoPriv,
319
+ authProtocol: AuthProtocols.sha,
320
+ authKey: 'authPassword',
321
+ };
322
+
323
+ // authPriv requires both authentication and privacy parameters
324
+ const user3 = {
325
+ name: 'user3',
326
+ level: SecurityLevel.authPriv,
327
+ authProtocol: AuthProtocols.sha,
328
+ authKey: 'authPassword',
329
+ privProtocol: PrivProtocols.aes,
330
+ privKey: 'privPassword',
331
+ };
332
+
333
+ // This function would typically be part of parameter validation
334
+ function validateUser(user) {
335
+ if (user.level === SecurityLevel.authNoPriv || user.level === SecurityLevel.authPriv) {
336
+ assert.ok(user.authProtocol, 'authProtocol required for this security level');
337
+ assert.ok(user.authKey, 'authKey required for this security level');
338
+ }
339
+
340
+ if (user.level === SecurityLevel.authPriv) {
341
+ assert.ok(user.privProtocol, 'privProtocol required for this security level');
342
+ assert.ok(user.privKey, 'privKey required for this security level');
343
+ }
344
+
345
+ return true;
346
+ }
347
+
348
+ assert.strictEqual(validateUser(user1), true);
349
+ assert.strictEqual(validateUser(user2), true);
350
+ assert.strictEqual(validateUser(user3), true);
351
+ });
352
+ });
353
+
354
+ describe('Custom engineID handling', function () {
355
+ it('should correctly use engineID parameter', function () {
356
+ // This test verifies the fix for issue #283
357
+ // Create a session with default settings (no engineID)
358
+ const defaultSession = new snmp.Session({
359
+ host: 'example.org',
360
+ version: snmp.Version3
361
+ });
362
+
363
+ // Default session should have an engineID of expected format (17 bytes)
364
+ assert.strictEqual(defaultSession.engine.engineID.length, 17);
365
+
366
+ // Convert to hex string for easier inspection
367
+ const defaultEngineIDHex = defaultSession.engine.engineID.toString('hex');
368
+ // First 5 bytes should match the standard format 8000B98380
369
+ assert.strictEqual(defaultEngineIDHex.substring(0, 10), '8000b98380');
370
+
371
+ // Create a second session - should generate a different random engineID
372
+ const anotherDefaultSession = new snmp.Session({
373
+ host: 'example.org',
374
+ version: snmp.Version3
375
+ });
376
+
377
+ // The two sessions should have different engineIDs (random part differs)
378
+ assert.notStrictEqual(
379
+ defaultSession.engine.engineID.toString('hex'),
380
+ anotherDefaultSession.engine.engineID.toString('hex')
381
+ );
382
+ });
383
+ });
384
+ });