Skip to content

Commit ed75080

Browse files
author
Stephan Brandauer
committed
add stringConcatenatedWith feature to help the model learn that string concatenation leaves are usually not sinks
1 parent 9468f62 commit ed75080

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ private newtype TEndpointFeature =
237237
TInputArgumentIndex() or
238238
TContextFunctionInterfaces() or
239239
TContextSurroundingFunctionParameters() or
240-
TAssignedToPropName()
240+
TAssignedToPropName() or
241+
TStringConcatenatedWith()
241242

242243
/**
243244
* An implementation of an endpoint feature: produces feature names and values for used in ML.
@@ -492,6 +493,51 @@ class AssignedToPropName extends EndpointFeature, TAssignedToPropName {
492493
}
493494
}
494495

496+
/**
497+
* The feature that shows the text an endpoint is being concatenated with.class
498+
*
499+
* ### Example
500+
*
501+
* ```javascript
502+
* const x = 'foo' + endpoint + 'bar'; // feature value is `'foo' -endpoint- 'bar'`
503+
*/
504+
class StringConcatenatedWith extends EndpointFeature, TStringConcatenatedWith {
505+
override string getName() { result = "stringConcatenatedWith" }
506+
507+
override string getValue(DataFlow::Node endpoint) {
508+
exists(StringOps::ConcatenationRoot root |
509+
root.getALeaf() = endpoint and
510+
result =
511+
concat(StringOps::ConcatenationLeaf p |
512+
p.getRoot() = root and
513+
(
514+
p.getStartLine() < endpoint.getStartLine()
515+
or
516+
p.getStartLine() = endpoint.getStartLine() and
517+
p.getStartColumn() < endpoint.getStartColumn()
518+
)
519+
|
520+
SyntacticUtilities::renderStringConcatOperand(p), " + "
521+
order by
522+
p.getStartLine(), p.getStartColumn()
523+
) + " -endpoint- " +
524+
concat(StringOps::ConcatenationLeaf p |
525+
p.getRoot() = root and
526+
(
527+
p.getStartLine() > endpoint.getStartLine()
528+
or
529+
p.getStartLine() = endpoint.getStartLine() and
530+
p.getStartColumn() > endpoint.getStartColumn()
531+
)
532+
|
533+
SyntacticUtilities::renderStringConcatOperand(p), " + "
534+
order by
535+
p.getStartLine(), p.getStartColumn()
536+
)
537+
)
538+
}
539+
}
540+
495541
/**
496542
* The feature for the imports used in the callee of an invocation.
497543
*
@@ -555,6 +601,23 @@ class ContextFunctionInterfaces extends EndpointFeature, TContextFunctionInterfa
555601
* Syntactic utilities for feature value computation.
556602
*/
557603
private module SyntacticUtilities {
604+
bindingset[start, end]
605+
string renderStringConcatOperands(DataFlow::Node root, int start, int end) {
606+
result =
607+
concat(int i, string operand |
608+
i = [start .. end] and
609+
operand = renderStringConcatOperand(StringConcatenation::getOperand(root, i))
610+
|
611+
operand, " + " order by i
612+
)
613+
}
614+
615+
string renderStringConcatOperand(DataFlow::Node operand) {
616+
if exists(unique(string v | operand.mayHaveStringValue(v)))
617+
then result = "'" + any(string v | operand.mayHaveStringValue(v)) + "'"
618+
else result = getSimpleAccessPath(operand)
619+
}
620+
558621
/** Gets all the imports defined in the file containing the endpoint. */
559622
string getImportPathsForFile(File file) {
560623
result =

javascript/ql/experimental/adaptivethreatmodeling/test/generic_feature_testing/FeatureValue.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,15 @@
188188
| test.js:22:21:22:28 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
189189
| test.js:22:21:22:28 | endpoint | enclosingFunctionName | |
190190
| test.js:22:21:22:28 | endpoint | fileImports | foo lib1 lib2 lib3 |
191+
| test.js:33:50:33:57 | endpoint | calleeAccessPath | |
192+
| test.js:33:50:33:57 | endpoint | calleeAccessPathWithStructuralInfo | |
193+
| test.js:33:50:33:57 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
194+
| test.js:33:50:33:57 | endpoint | contextSurroundingFunctionParameters | |
195+
| test.js:33:50:33:57 | endpoint | fileImports | foo lib1 lib2 lib3 |
196+
| test.js:33:50:33:57 | endpoint | stringConcatenatedWith | f() + '<a target="_blank" href="' -endpoint- '"></a>' |
197+
| test.js:35:18:35:25 | endpoint | calleeAccessPath | |
198+
| test.js:35:18:35:25 | endpoint | calleeAccessPathWithStructuralInfo | |
199+
| test.js:35:18:35:25 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
200+
| test.js:35:18:35:25 | endpoint | contextSurroundingFunctionParameters | |
201+
| test.js:35:18:35:25 | endpoint | fileImports | foo lib1 lib2 lib3 |
202+
| test.js:35:18:35:25 | endpoint | stringConcatenatedWith | 'foo' -endpoint- 'bar' |

javascript/ql/experimental/adaptivethreatmodeling/test/generic_feature_testing/test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,9 @@ function f({ endpoint }) {}
2727
const g = async () => undefined;
2828

2929
const o = { m: () => undefined }
30+
31+
const url = f();
32+
33+
const x = f() + "<a target=\"_blank\" href=\"" + endpoint + "\"></a>";
34+
35+
const y = "foo"+ endpoint + "bar";

0 commit comments

Comments
 (0)