Skip to content

Commit b01bb58

Browse files
committed
Fixed: Hardened tokenize/parse, esp. comment parsing, see #713
1 parent 47bb95a commit b01bb58

File tree

2 files changed

+90
-86
lines changed

2 files changed

+90
-86
lines changed

src/parse.js

Lines changed: 69 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -257,18 +257,34 @@ function parse(source, root, options) {
257257
return false;
258258
}
259259

260+
function ifBlock(obj, fn) {
261+
var trailingLine = tn.line();
262+
obj.comment = cmnt(); // try block-type comment
263+
obj.filename = parse.filename;
264+
if (skip("{", true)) {
265+
fn();
266+
skip(";", true);
267+
return true;
268+
}
269+
skip(";");
270+
if (typeof obj.comment !== 'string')
271+
obj.comment = cmnt(trailingLine); // try line-type comment if no block
272+
return false;
273+
}
274+
260275
function parseType(parent, token) {
261-
var name = next();
276+
262277
/* istanbul ignore next */
263-
if (!nameRe.test(name))
264-
throw illegal(name, "type name");
265-
var type = new Type(name);
266-
type.comment = cmnt();
267-
type.filename = parse.filename;
268-
if (skip("{", true)) {
278+
if (!nameRe.test(token = next()))
279+
throw illegal(token, "type name");
280+
281+
var type = new Type(token);
282+
ifBlock(type, function() {
269283
while ((token = next()) !== "}") {
284+
270285
if (parseCommon(type, token))
271286
continue;
287+
272288
switch (token) {
273289

274290
case "map":
@@ -302,9 +318,7 @@ function parse(source, root, options) {
302318
break;
303319
}
304320
}
305-
skip(";", true);
306-
} else
307-
skip(";");
321+
});
308322
parent.add(type);
309323
}
310324

@@ -314,22 +328,26 @@ function parse(source, root, options) {
314328
parseGroup(parent, rule);
315329
return;
316330
}
331+
317332
/* istanbul ignore next */
318333
if (!typeRefRe.test(type))
319334
throw illegal(type, "type");
335+
320336
var name = next();
337+
321338
/* istanbul ignore next */
322339
if (!nameRe.test(name))
323340
throw illegal(name, "name");
341+
324342
name = applyCase(name);
325343
skip("=");
326344
var field = new Field(name, parseId(next()), type, rule, extend),
327345
trailingLine = tn.line();
328-
field.comment = cmnt();
346+
field.comment = cmnt(); // try block-type
329347
field.filename = parse.filename;
330348
parseInlineOptions(field);
331349
if (!field.comment)
332-
field.comment = cmnt(trailingLine);
350+
field.comment = cmnt(trailingLine); // try line-type
333351
// JSON defaults to packed=true if not set so we have to set packed=false explicity when
334352
// parsing proto2 descriptors without the option, where applicable. This must be done for
335353
// any type (not just packable types) because enums also use varint encoding and it is not
@@ -341,39 +359,43 @@ function parse(source, root, options) {
341359

342360
function parseGroup(parent, rule) {
343361
var name = next();
362+
344363
/* istanbul ignore next */
345364
if (!nameRe.test(name))
346365
throw illegal(name, "name");
366+
347367
var fieldName = util.lcFirst(name);
348368
if (name === fieldName)
349369
name = util.ucFirst(name);
350370
skip("=");
351371
var id = parseId(next());
352372
var type = new Type(name);
353373
type.group = true;
354-
type.comment = cmnt();
355374
var field = new Field(fieldName, id, name, rule);
356-
type.filename = field.filename = parse.filename;
357-
skip("{");
358-
while ((token = next()) !== "}") {
359-
switch (token) {
360-
case "option":
361-
parseOption(type, token);
362-
skip(";");
363-
break;
364-
case "required":
365-
case "optional":
366-
case "repeated":
367-
parseField(type, token);
368-
break;
375+
field.filename = parse.filename;
376+
ifBlock(type, function() {
377+
while ((token = next()) !== "}") {
378+
switch (token) {
369379

370-
/* istanbul ignore next */
371-
default:
372-
throw illegal(token); // there are no groups with proto3 semantics
380+
case "option":
381+
parseOption(type, token);
382+
skip(";");
383+
break;
384+
385+
case "required":
386+
case "optional":
387+
case "repeated":
388+
parseField(type, token);
389+
break;
390+
391+
/* istanbul ignore next */
392+
default:
393+
throw illegal(token); // there are no groups with proto3 semantics
394+
}
373395
}
374-
}
375-
skip(";", true);
376-
parent.add(type).add(field);
396+
});
397+
parent.add(type)
398+
.add(field);
377399
}
378400

379401
function parseMapField(parent) {
@@ -398,11 +420,11 @@ function parse(source, root, options) {
398420
skip("=");
399421
var field = new MapField(name, parseId(next()), keyType, valueType),
400422
trailingLine = tn.line();
401-
field.comment = cmnt();
423+
field.comment = cmnt(); // try block-type
402424
field.filename = parse.filename;
403425
parseInlineOptions(field);
404426
if (!field.comment)
405-
field.comment = cmnt(trailingLine);
427+
field.comment = cmnt(trailingLine); // try line-type
406428
parent.add(field);
407429
}
408430

@@ -414,11 +436,8 @@ function parse(source, root, options) {
414436
throw illegal(name, "name");
415437

416438
name = applyCase(name);
417-
var oneof = new OneOf(name),
418-
trailingLine = tn.line();
419-
oneof.comment = cmnt();
420-
oneof.filename = parse.filename;
421-
if (skip("{", true)) {
439+
var oneof = new OneOf(name);
440+
ifBlock(oneof, function() {
422441
while ((token = next()) !== "}") {
423442
if (token === "option") {
424443
parseOption(oneof, token);
@@ -428,12 +447,7 @@ function parse(source, root, options) {
428447
parseField(oneof, "optional");
429448
}
430449
}
431-
skip(";", true);
432-
} else {
433-
skip(";");
434-
if (!oneof.comment)
435-
oneof.comment = cmnt(trailingLine);
436-
}
450+
});
437451
parent.add(oneof);
438452
}
439453

@@ -445,19 +459,15 @@ function parse(source, root, options) {
445459
throw illegal(name, "name");
446460

447461
var enm = new Enum(name);
448-
enm.comment = cmnt();
449-
enm.filename = parse.filename;
450-
if (skip("{", true)) {
462+
ifBlock(enm, function() {
451463
while ((token = next()) !== "}") {
452464
if (token === "option") {
453465
parseOption(enm, token);
454466
skip(";");
455467
} else
456468
parseEnumValue(enm, token);
457469
}
458-
skip(";", true);
459-
} else
460-
skip(";");
470+
});
461471
parent.add(enm);
462472
}
463473

@@ -471,7 +481,7 @@ function parse(source, root, options) {
471481
skip("=");
472482
var value = parseId(next(), true),
473483
trailingLine = tn.line();
474-
parent.add(name, value, cmnt());
484+
parent.add(name, value, cmnt()); // block-type only
475485
parseInlineOptions({}); // skips enum value options
476486
if (!parent.comments[name])
477487
parent.comments[name] = cmnt(trailingLine);
@@ -539,11 +549,8 @@ function parse(source, root, options) {
539549
if (!nameRe.test(token))
540550
throw illegal(token, "service name");
541551

542-
var name = token;
543-
var service = new Service(name);
544-
service.comment = cmnt();
545-
service.filename = parse.filename;
546-
if (skip("{", true)) {
552+
var service = new Service(token);
553+
ifBlock(service, function() {
547554
while ((token = next()) !== "}") {
548555
switch (token) {
549556
case "option":
@@ -559,9 +566,7 @@ function parse(source, root, options) {
559566
throw illegal(token);
560567
}
561568
}
562-
skip(";", true);
563-
} else
564-
skip(";");
569+
});
565570
parent.add(service);
566571
}
567572

@@ -590,13 +595,11 @@ function parse(source, root, options) {
590595

591596
responseType = token;
592597
skip(")");
593-
var method = new Method(name, type, requestType, responseType, requestStream, responseStream),
594-
trailingLine = tn.line();
595-
method.comment = cmnt();
596-
method.filename = parse.filename;
597-
if (skip("{", true)) {
598+
var method = new Method(name, type, requestType, responseType, requestStream, responseStream);
599+
ifBlock(method, function() {
598600
while ((token = next()) !== "}") {
599601
switch (token) {
602+
600603
case "option":
601604
parseOption(method, token);
602605
skip(";");
@@ -607,12 +610,7 @@ function parse(source, root, options) {
607610
throw illegal(token);
608611
}
609612
}
610-
skip(";", true);
611-
} else {
612-
skip(";");
613-
if (!method.comment)
614-
method.comment = cmnt(trailingLine);
615-
}
613+
});
616614
parent.add(method);
617615
}
618616

src/tokenize.js

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,26 @@ function tokenize(source) {
244244
return false;
245245
}
246246

247+
/**
248+
* Gets a comment.
249+
* @param {number=} trailingLine Trailing line number if applicable
250+
* @returns {?string} Comment text
251+
* @inner
252+
*/
253+
function cmnt(trailingLine) {
254+
var ret;
255+
if (trailingLine === undefined)
256+
ret = commentLine === line - 1 && commentText || null;
257+
else {
258+
if (!commentText)
259+
peek();
260+
ret = commentLine === trailingLine && commentType === "/" && commentText || null;
261+
}
262+
commentType = commentText = null;
263+
commentLine = 0;
264+
return ret;
265+
}
266+
247267
return {
248268
next: next,
249269
peek: peek,
@@ -252,21 +272,7 @@ function tokenize(source) {
252272
line: function() {
253273
return line;
254274
},
255-
cmnt: function(trailingLine) {
256-
var ret;
257-
if (trailingLine === undefined)
258-
ret = commentLine === line - 1 && commentText || null;
259-
else {
260-
if (!commentText)
261-
peek();
262-
ret = commentLine === trailingLine && commentType === "/" && commentText || null;
263-
}
264-
if (ret) {
265-
commentType = commentText = null;
266-
commentLine = 0;
267-
}
268-
return ret;
269-
}
275+
cmnt: cmnt
270276
};
271277
/* eslint-enable callback-return */
272278
}

0 commit comments

Comments
 (0)