Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6c3d1d6

Browse files
authored
Merge pull request #1694 from asger-semmle/concatenation-operand
Approved by xiemaisi
2 parents 7482233 + d83f152 commit 6c3d1d6

9 files changed

Lines changed: 796 additions & 41 deletions

File tree

javascript/ql/src/semmle/javascript/StringConcatenation.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module StringConcatenation {
1313
}
1414

1515
/** Gets the `n`th operand to the string concatenation defining `node`. */
16+
pragma[nomagic]
1617
DataFlow::Node getOperand(DataFlow::Node node, int n) {
1718
exists(AddExpr add | node = add.flow() |
1819
n = 0 and result = add.getLeftOperand().flow()

javascript/ql/src/semmle/javascript/StringOps.qll

Lines changed: 258 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -438,51 +438,96 @@ module StringOps {
438438
}
439439

440440
/**
441-
* A data flow node that concatenates strings and returns the result.
441+
* Holds if `first` and `second` are adjacent leaves in a concatenation tree.
442442
*/
443-
class Concatenation extends DataFlow::Node {
444-
Concatenation() {
443+
pragma[nomagic]
444+
private predicate adjacentLeaves(ConcatenationLeaf first, ConcatenationLeaf second) {
445+
exists(Concatenation parent, int i |
446+
first = parent.getOperand(i).getLastLeaf() and
447+
second = parent.getOperand(i + 1).getFirstLeaf()
448+
)
449+
}
450+
451+
/**
452+
* A data flow node that performs a string concatenation or occurs as an operand
453+
* in a string concatenation.
454+
*
455+
* For example, the expression `x + y + z` contains the following concatenation
456+
* nodes:
457+
* - The leaf nodes `x`, `y`, and `z`
458+
* - The intermediate node `x + y`, which is both a concatenation and an operand
459+
* - The root node `x + y + z`
460+
*
461+
*
462+
* Note that the following are not recognized a string concatenations:
463+
* - Array `join()` calls with a non-empty separator
464+
* - Tagged template literals
465+
*
466+
*
467+
* Also note that all `+` operators are seen as string concatenations,
468+
* even in cases where it is used for arithmetic.
469+
*
470+
* Examples of string concatenations:
471+
* ```
472+
* x + y
473+
* x += y
474+
* [x, y].join('')
475+
* Array(x, y).join('')
476+
* `Hello, ${message}`
477+
* ```
478+
*/
479+
class ConcatenationNode extends DataFlow::Node {
480+
pragma[inline]
481+
ConcatenationNode() {
445482
exists(StringConcatenation::getAnOperand(this))
483+
or
484+
this = StringConcatenation::getAnOperand(_)
446485
}
447486

448487
/**
449488
* Gets the `n`th operand of this string concatenation.
450489
*/
451-
DataFlow::Node getOperand(int n) {
490+
pragma[inline]
491+
ConcatenationOperand getOperand(int n) {
452492
result = StringConcatenation::getOperand(this, n)
453493
}
454494

455495
/**
456496
* Gets an operand of this string concatenation.
457497
*/
458-
DataFlow::Node getAnOperand() {
498+
pragma[inline]
499+
ConcatenationOperand getAnOperand() {
459500
result = StringConcatenation::getAnOperand(this)
460501
}
461502

462503
/**
463504
* Gets the number of operands of this string concatenation.
464505
*/
506+
pragma[inline]
465507
int getNumOperand() {
466508
result = StringConcatenation::getNumOperand(this)
467509
}
468510

469511
/**
470512
* Gets the first operand of this string concatenation.
471513
*/
472-
DataFlow::Node getFirstOperand() {
514+
pragma[inline]
515+
ConcatenationOperand getFirstOperand() {
473516
result = StringConcatenation::getFirstOperand(this)
474517
}
475518

476519
/**
477520
* Gets the last operand of this string concatenation
478521
*/
479-
DataFlow::Node getLastOperand() {
522+
pragma[inline]
523+
ConcatenationOperand getLastOperand() {
480524
result = StringConcatenation::getLastOperand(this)
481525
}
482526

483527
/**
484528
* Holds if this only acts as a string coercion, such as `"" + x`.
485529
*/
530+
pragma[inline]
486531
predicate isCoercion() {
487532
StringConcatenation::isCoercion(this)
488533
}
@@ -492,15 +537,220 @@ module StringOps {
492537
* it is a concatenation operator that is not itself the immediate operand to
493538
* another concatenation operator.
494539
*/
540+
pragma[inline]
495541
predicate isRoot() {
496542
StringConcatenation::isRoot(this)
497543
}
498544

545+
/**
546+
* Holds if this is a leaf in the concatenation tree, that is, it is not
547+
* itself a concatenation.
548+
*/
549+
pragma[inline]
550+
predicate isLeaf() {
551+
not exists(StringConcatenation::getAnOperand(this))
552+
}
553+
499554
/**
500555
* Gets the root of the concatenation tree in which this is an operator.
501556
*/
502-
Concatenation getRoot() {
557+
pragma[inline]
558+
ConcatenationRoot getRoot() {
503559
result = StringConcatenation::getRoot(this)
504560
}
561+
562+
/**
563+
* Gets the enclosing concatenation in which this is an operand, if any.
564+
*/
565+
pragma[inline]
566+
Concatenation getParentConcatenation() {
567+
this = StringConcatenation::getAnOperand(result)
568+
}
569+
570+
/**
571+
* Gets the last leaf in this concatenation tree.
572+
*
573+
* For example, `z` is the last leaf in `x + y + z`.
574+
*/
575+
pragma[inline]
576+
ConcatenationLeaf getLastLeaf() {
577+
result = StringConcatenation::getLastOperand*(this)
578+
}
579+
580+
/**
581+
* Gets the first leaf in this concatenation tree.
582+
*
583+
* For example, `x` is the first leaf in `x + y + z`.
584+
*/
585+
pragma[inline]
586+
ConcatenationLeaf getFirstLeaf() {
587+
result = StringConcatenation::getFirstOperand*(this)
588+
}
589+
590+
/**
591+
* Gets the leaf that is occurs immediately before this leaf in the
592+
* concatenation tree, if any.
593+
*
594+
* For example, `y` is the previous leaf from `z` in `x + y + z`.
595+
*/
596+
pragma[inline]
597+
ConcatenationLeaf getPreviousLeaf() {
598+
adjacentLeaves(result, this)
599+
}
600+
601+
/**
602+
* Gets the leaf that is occurs immediately after this leaf in the
603+
* concatenation tree, if any.
604+
*
605+
* For example, `y` is the next leaf from `x` in `x + y + z`.
606+
*/
607+
pragma[inline]
608+
ConcatenationLeaf getNextLeaf() {
609+
adjacentLeaves(this, result)
610+
}
611+
}
612+
613+
/**
614+
* A data flow node that performs a string concatenation and returns the result.
615+
*
616+
* Examples:
617+
* ```
618+
* x + y
619+
* x += y
620+
* [x, y].join('')
621+
* Array(x, y).join('')
622+
* `Hello ${message}`
623+
* ```
624+
*
625+
* See `ConcatenationNode` for more information.
626+
*/
627+
class Concatenation extends ConcatenationNode {
628+
pragma[inline]
629+
Concatenation() {
630+
exists(StringConcatenation::getAnOperand(this))
631+
}
632+
}
633+
634+
/**
635+
* One of the operands in a string concatenation.
636+
*
637+
* Examples:
638+
* ```
639+
* x + y // x and y are operands
640+
* [x, y].join('') // x and y are operands
641+
* `Hello ${message}` // `Hello ` and message are operands
642+
* ```
643+
*
644+
* See `ConcatenationNode` for more information.
645+
*/
646+
class ConcatenationOperand extends ConcatenationNode {
647+
pragma[inline]
648+
ConcatenationOperand() {
649+
this = StringConcatenation::getAnOperand(_)
650+
}
651+
}
652+
653+
/**
654+
* A data flow node that performs a string concatenation, and is not an
655+
* immediate operand in a larger string concatenation.
656+
*
657+
* Examples:
658+
* ```
659+
* // x + y + z is a root, but the inner x + y is not
660+
* return x + y + z;
661+
* ```
662+
*
663+
* See `ConcatenationNode` for more information.
664+
*/
665+
class ConcatenationRoot extends Concatenation {
666+
pragma[inline]
667+
ConcatenationRoot() {
668+
isRoot()
669+
}
670+
671+
/**
672+
* Gets a leaf in this concatenation tree that this node is the root of.
673+
*/
674+
pragma[inline]
675+
ConcatenationLeaf getALeaf() {
676+
this = StringConcatenation::getRoot(result)
677+
}
678+
679+
/**
680+
* Returns the concatenation of all constant operands in this concatenation,
681+
* ignoring the non-constant parts entirely.
682+
*
683+
* For example, for the following concatenation
684+
* ```
685+
* `Hello ${person}, how are you?`
686+
* ```
687+
* the result is `"Hello , how are you?"`
688+
*/
689+
string getConstantStringParts() {
690+
result = getStringValue()
691+
or
692+
not exists(getStringValue()) and
693+
result = strictconcat(StringLiteralLike leaf |
694+
leaf = getALeaf().asExpr()
695+
|
696+
leaf.getStringValue()
697+
order by leaf.getFirstToken().getIndex() asc
698+
)
699+
}
700+
}
701+
702+
/** A string literal or template literal without any substitutions. */
703+
private class StringLiteralLike extends Expr {
704+
StringLiteralLike() {
705+
this instanceof StringLiteral or
706+
this instanceof TemplateElement
707+
}
708+
}
709+
710+
/**
711+
* An operand to a concatenation that is not itself a concatenation.
712+
*
713+
* Example:
714+
* ```
715+
* x + y + z // x, y, and z are leaves
716+
* [x, y + z].join('') // x, y, and z are leaves
717+
* ```
718+
*
719+
* See `ConcatenationNode` for more information.
720+
*/
721+
class ConcatenationLeaf extends ConcatenationOperand {
722+
pragma[inline]
723+
ConcatenationLeaf() {
724+
isLeaf()
725+
}
726+
}
727+
728+
/**
729+
* The root node in a concatenation of one or more strings containing HTML fragments.
730+
*/
731+
class HtmlConcatenationRoot extends ConcatenationRoot {
732+
pragma[noinline]
733+
HtmlConcatenationRoot() {
734+
getConstantStringParts().regexpMatch("(?s).*</?[a-zA-Z][^\\r\\n<>/]*/?>.*")
735+
}
736+
}
737+
738+
/**
739+
* A data flow node that is part of an HTML string concatenation.
740+
*/
741+
class HtmlConcatenationNode extends ConcatenationNode {
742+
HtmlConcatenationNode() {
743+
getRoot() instanceof HtmlConcatenationRoot
744+
}
745+
}
746+
747+
/**
748+
* A data flow node that is part of an HTML string concatenation,
749+
* and is not itself a concatenation operator.
750+
*/
751+
class HtmlConcatenationLeaf extends ConcatenationLeaf {
752+
HtmlConcatenationLeaf() {
753+
getRoot() instanceof HtmlConcatenationRoot
754+
}
505755
}
506756
}

javascript/ql/src/semmle/javascript/Templates.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class TemplateLiteral extends Expr, @templateliteral {
4242
*/
4343
Expr getAnElement() { result = getElement(_) }
4444

45+
/**
46+
* Gets the number of elements of this template literal.
47+
*/
48+
int getNumElement() { result = count(getAnElement()) }
49+
4550
override predicate isImpure() { getAnElement().isImpure() }
4651

4752
override string getStringValue() {

0 commit comments

Comments
 (0)