drachtio-srf 5.0.21 → 5.0.23

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.
@@ -42,7 +42,12 @@ module.exports = class DigestClient {
42
42
  if (!this.res.has(header)) {
43
43
  return callback(new Error(`missing ${header} in ${this.res.statusCode} response`));
44
44
  }
45
- const challenge = this._parseChallenge(this.res.get(header));
45
+ const rawChallenge = this.res.get(header);
46
+ if (typeof rawChallenge !== 'string' || rawChallenge.length === 0) {
47
+ return callback(new Error(
48
+ `empty or non-string ${header} value in ${this.res.statusCode} response`));
49
+ }
50
+ const challenge = this._parseChallenge(rawChallenge);
46
51
 
47
52
  const ha1 = crypto.createHash('md5');
48
53
  ha1.update([username, challenge.realm, password].join(':'));
@@ -155,13 +160,16 @@ module.exports = class DigestClient {
155
160
  }
156
161
 
157
162
  _parseChallenge(digest) {
163
+ const params = {};
164
+ if (typeof digest !== 'string') return params;
158
165
  const prefix = 'Digest ';
159
- const challenge = digest.substr(digest.indexOf(prefix) + prefix.length);
166
+ const prefixIdx = digest.indexOf(prefix);
167
+ if (prefixIdx === -1) return params;
168
+ const challenge = digest.substr(prefixIdx + prefix.length);
160
169
  const parts = challenge.split(',');
161
170
  const length = parts.length;
162
- const params = {};
163
171
  for (let i = 0; i < length; i++) {
164
- const part = parts[i].match(/^\s*?([a-zA-Z0-0]+)="?(.*?)"?\s*?$/);
172
+ const part = parts[i].match(/^\s*?([a-zA-Z0-9]+)="?(.*?)"?\s*?$/);
165
173
  if (part && part.length > 2) {
166
174
  params[part[1]] = part[2];
167
175
  }
@@ -301,7 +301,14 @@ class DrachtioAgent extends Emitter {
301
301
  transactionId: transactionId
302
302
  };
303
303
 
304
- const req = new Request(new SipMessage(msg), meta);
304
+ let sipMsg;
305
+ try {
306
+ sipMsg = new SipMessage(msg);
307
+ } catch(err) {
308
+ console.error(err, `unable to parse echoed sent request: ${msg}`);
309
+ return params.callback(err);
310
+ }
311
+ const req = new Request(sipMsg, meta);
305
312
  req.agent = this;
306
313
  req.socket = obj.socket;
307
314
  if (params.options.auth) {
@@ -356,7 +363,14 @@ class DrachtioAgent extends Emitter {
356
363
  obj.pendingRequests.set(msgId, (token, msg, meta) => {
357
364
  obj.pendingRequests.delete(msgId);
358
365
  if ('OK' !== token[0]) { if (callback) return callback(token[1]); return; }
359
- const responseMsg = new SipMessage(msg);
366
+ let responseMsg;
367
+ try {
368
+ responseMsg = new SipMessage(msg);
369
+ } catch(err) {
370
+ console.error(err, `unable to parse echoed sent response: ${msg}`);
371
+ if (callback) callback(err);
372
+ return;
373
+ }
360
374
  res.meta = meta;
361
375
  if (callback) {
362
376
  callback(null, responseMsg);
@@ -401,7 +415,14 @@ class DrachtioAgent extends Emitter {
401
415
  if ('OK' !== token[0]) {
402
416
  return callback(token[1]);
403
417
  }
404
- callback(null, new SipMessage(msg));
418
+ let sipMsg;
419
+ try {
420
+ sipMsg = new SipMessage(msg);
421
+ } catch(err) {
422
+ console.error(err, `unable to parse echoed sent ACK: ${msg}`);
423
+ return callback(err);
424
+ }
425
+ callback(null, sipMsg);
405
426
  });
406
427
  }
407
428
  }
@@ -808,7 +829,13 @@ class DrachtioAgent extends Emitter {
808
829
  const msgSource = token[2];
809
830
  const msgTime = token[3];
810
831
  rawMsg = msg.slice(pos + 2);
811
- const cdrSipMsg = new SipMessage(rawMsg);
832
+ let cdrSipMsg;
833
+ try {
834
+ cdrSipMsg = new SipMessage(rawMsg);
835
+ } catch(err) {
836
+ console.error(err, `unable to parse CDR sip message: ${rawMsg}`);
837
+ return;
838
+ }
812
839
  const args = [msgSource, msgTime];
813
840
  if (cdrEvent !== 'attempt') { args.push(token[4]); }
814
841
  args.push(cdrSipMsg);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drachtio-srf",
3
- "version": "5.0.21",
3
+ "version": "5.0.23",
4
4
  "description": "drachtio signaling resource framework",
5
5
  "main": "lib/srf.js",
6
6
  "types": "lib/@types/index.d.ts",
@@ -134,3 +134,66 @@ describe('DigestClient proxy pinning after challenge', function() {
134
134
  });
135
135
 
136
136
  });
137
+
138
+ describe('DigestClient malformed challenge handling', function() {
139
+
140
+ function resWithChallenge(value) {
141
+ const options = {
142
+ method: 'INVITE',
143
+ uri: 'sip:u@sbc.example.com',
144
+ auth: {username: 'u', password: 'p'},
145
+ headers: {}
146
+ };
147
+ const req = {
148
+ method: 'INVITE',
149
+ _originalParams: {options},
150
+ get: () => undefined,
151
+ getParsedHeader: () => ({seq: 1, method: 'INVITE'})
152
+ };
153
+ return {
154
+ statusCode: 401,
155
+ source_address: '1.1.1.1',
156
+ source_port: 5060,
157
+ socket: {},
158
+ req,
159
+ agent: {request: () => {}},
160
+ has: (h) => h.toLowerCase() === 'www-authenticate',
161
+ get: (h) => h.toLowerCase() === 'www-authenticate' ? value : undefined
162
+ };
163
+ }
164
+
165
+ it('returns Error (does not throw) when www-authenticate value is undefined', function(done) {
166
+ new DigestClient(resWithChallenge(undefined)).authenticate((err) => {
167
+ err.should.be.an.Error();
168
+ err.message.should.match(/empty or non-string/);
169
+ done();
170
+ });
171
+ });
172
+
173
+ it('returns Error when www-authenticate value is empty string', function(done) {
174
+ new DigestClient(resWithChallenge('')).authenticate((err) => {
175
+ err.should.be.an.Error();
176
+ err.message.should.match(/empty or non-string/);
177
+ done();
178
+ });
179
+ });
180
+
181
+ it('_parseChallenge returns {} for undefined input', function() {
182
+ const out = new DigestClient(resWithChallenge('Digest realm="x",nonce="y"'))
183
+ ._parseChallenge(undefined);
184
+ out.should.eql({});
185
+ });
186
+
187
+ it('_parseChallenge returns {} for non-Digest input', function() {
188
+ const out = new DigestClient(resWithChallenge('Digest realm="x",nonce="y"'))
189
+ ._parseChallenge('Basic realm="x"');
190
+ out.should.eql({});
191
+ });
192
+
193
+ it('_parseChallenge captures digit-containing directive names (md5 algorithm)', function() {
194
+ const out = new DigestClient(resWithChallenge('Digest realm="x",nonce="y"'))
195
+ ._parseChallenge('Digest realm="x",nonce="y",algorithm=MD5');
196
+ out.algorithm.should.eql('MD5');
197
+ });
198
+
199
+ });