-
Notifications
You must be signed in to change notification settings - Fork 1.9k
JS: Modeling of underscore.string package
#19049
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
e8b233f
Added test cases `underscore.string` string to string.
Napalys 9bca863
Added modeling of `underscore.string` string to string functions.
Napalys c256b9c
Added `underscore.string` test cases for str to array.
Napalys 30623cd
Added modeling of `underscore.string` for str to array.
Napalys cd40b6f
Added test cases `underscore.string` array to string.
Napalys 6b105b2
Added modeling `underscore.string` array to string functions.
Napalys 77e1e17
Added test cases `underscore.string` with multiple sources.
Napalys b59b9c8
Added modeling `underscore.string` of function which contain multiple…
Napalys 25c6fb5
Added chaining tests for `underscore.string` package.
Napalys ca9ae8a
Added chaining modeling for `underscore.string` package.
Napalys cccd863
Added test for extra chaining function for `underscore.string`.
Napalys 3a83c8d
Added modeling for extra chaining function from `underscore.string`.
Napalys dcc1e88
Added test cases for aliases.
Napalys fc6b779
Added modeling for aliases.
Napalys eb18c3c
Added test case for `tap`.
Napalys d8e6d76
Added modeling for `tap` function.
Napalys 2c7562d
Removed `value` from modeling its return value as Wrapper class, sinc…
Napalys 8b431dc
Added change note.
Napalys 922a07d
Added `underscore.string` `clearsContent`.
Napalys 752f02f
Fixed `map` modeling and added test cases.
Napalys f4ca2dc
Restricted taint to array elements.
Napalys 221cc19
Merge branch 'main' into js/underscore-string
Napalys ca53e97
Adressed comments.
Napalys 9e78755
Fixed typo in the test cases.
Napalys File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
javascript/ql/lib/change-notes/2025-03-17-underscore-string.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| category: minorAnalysis | ||
| --- | ||
| * Added support for the `underscore.string` package. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| extensions: | ||
| - addsTo: | ||
| pack: codeql/javascript-all | ||
| extensible: typeModel | ||
| data: | ||
| - ["'underscore.string'.Wrapper", "'underscore.string'", "ReturnValue"] | ||
| - ["'underscore.string'.Wrapper", "'underscore.string'.Wrapper", "Member[slugify,capitalize,decapitalize,clean,cleanDiacritics,swapCase,escapeHTML,unescapeHTML,wrap,dedent,reverse,pred,succ,titleize,camelize,classify,underscored,dasherize,humanize,trim,ltrim,rtrim,truncate,sprintf,strRight,strRightBack,strLeft,strLeftBack,stripTags,unquote,strip,lstrip,rstrip,camelcase].ReturnValue"] | ||
| - ["'underscore.string'.Wrapper", "'underscore.string'.Wrapper", "Member[insert,replaceAll,join,splice,prune,pad,lpad,rpad,repeat,surround,quote,q,rjust,ljust].ReturnValue"] | ||
| - ["'underscore.string'.Wrapper", "'underscore.string'.Wrapper", "Member[toUpperCase,toLowerCase,replace,slice,substring,substr,concat].ReturnValue"] | ||
| - ["'underscore.string'.Wrapper", "'underscore.string'.Wrapper", "Member[tap].ReturnValue"] | ||
|
|
||
| - addsTo: | ||
| pack: codeql/javascript-all | ||
| extensible: summaryModel | ||
| data: | ||
| - ["'underscore.string'", "Member[slugify,capitalize,decapitalize,clean,cleanDiacritics,swapCase,escapeHTML,unescapeHTML,wrap,dedent,reverse,pred,succ,titleize,camelize,classify,underscored,dasherize,humanize,trim,ltrim,rtrim,truncate,sprintf,strRight,strRightBack,strLeft,strLeftBack,stripTags,unquote,strip,lstrip,rstrip,camelcase]", "Argument[0]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'", "Member[chop,chars,words,lines]", "Argument[0]", "ReturnValue.ArrayElement", "taint"] | ||
| - ["'underscore.string'", "Member[toSentence,toSentenceSerial]", "Argument[0].ArrayElement", "ReturnValue", "taint"] | ||
| - ["'underscore.string'", "Member[insert,replaceAll,splice,prune,pad,lpad,rpad,repeat,rjust,ljust]", "Argument[0,2]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'", "Member[splice]", "Argument[0,3]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'", "Member[join]", "Argument[0..]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'", "Member[surround,quote,q]", "Argument[0,1]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'", "", "Argument[0]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'.Wrapper", "Member[slugify,capitalize,decapitalize,clean,cleanDiacritics,swapCase,escapeHTML,unescapeHTML,wrap,dedent,reverse,pred,succ,titleize,camelize,classify,underscored,dasherize,humanize,trim,ltrim,rtrim,truncate,sprintf,strRight,strRightBack,strLeft,strLeftBack,stripTags,unquote,value,strip,lstrip,rstrip,camelcase]", "Argument[this]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'.Wrapper", "Member[insert,replaceAll,join,splice,prune,pad,lpad,rpad,repeat,surround,quote,q,rjust,ljust]", "Argument[this]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'.Wrapper", "Member[insert,replaceAll,prune,pad,lpad,rpad,repeat,rjust,ljust]", "Argument[1]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'.Wrapper", "Member[surround,quote,q]", "Argument[0]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'.Wrapper", "Member[splice]", "Argument[2]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'.Wrapper", "Member[join,concat]", "Argument[0..]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'.Wrapper", "Member[toUpperCase,toLowerCase,replace,slice,substring,substr,split]", "Argument[this]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'.Wrapper", "Member[tap]", "Argument[this]", "ReturnValue", "taint"] | ||
| - ["'underscore.string'.Wrapper", "Member[tap]", "Argument[0].ReturnValue", "ReturnValue", "taint"] | ||
| - ["'underscore.string'.Wrapper", "Member[tap]", "Argument[this]", "Argument[0].Parameter[1]", "taint"] | ||
| - ["'underscore.string'", "Member[map]", "Argument[0]", "Argument[1].Parameter[0]", "taint"] | ||
| - ["'underscore.string'", "Member[map]", "Argument[1].ReturnValue", "ReturnValue", "taint"] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
javascript/ql/lib/semmle/javascript/frameworks/UnderscoreDotString.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /** | ||
| * Provides classes for modeling data flow behavior of the Underscore.string library (https://www.npmjs.com/package/underscore.string). | ||
| */ | ||
|
|
||
| private import javascript | ||
| private import semmle.javascript.dataflow.internal.AdditionalFlowInternal | ||
|
|
||
| /** | ||
| * Models data flow for the Underscore.string library. | ||
| */ | ||
| private class UnderscoreDotString extends AdditionalFlowInternal { | ||
| /** | ||
| * Some of the methods in `underscore.string` have the same name as methods from `Array.prototype`. | ||
| * This prevents methods like `splice` from propagating into Argument[this].ArrayElement. | ||
| */ | ||
| override predicate clearsContent(DataFlow::Node node, DataFlow::ContentSet contents) { | ||
|
||
| exists(DataFlow::CallNode call | | ||
| call = | ||
| ModelOutput::getATypeNode(["'underscore.string'.Wrapper", "'underscore.string'"]) | ||
| .getAMember() | ||
| .getACall() and | ||
| node = call.getReceiver().getPostUpdateNode() and | ||
| contents = DataFlow::ContentSet::arrayElement() | ||
| ) | ||
| } | ||
| } | ||
130 changes: 130 additions & 0 deletions
130
javascript/ql/test/library-tests/TripleDot/underscore.string.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| var s = require("underscore.string"); | ||
|
|
||
| function strToStr() { | ||
| sink(s.slugify(source("s1"))); // $ hasTaintFlow=s1 | ||
| sink(s.capitalize(source("s2"))); // $ hasTaintFlow=s2 | ||
| sink(s.decapitalize(source("s3"))); // $ hasTaintFlow=s3 | ||
| sink(s.clean(source("s4"))); // $ hasTaintFlow=s4 | ||
| sink(s.cleanDiacritics(source("s5"))); // $ hasTaintFlow=s5 | ||
| sink(s.swapCase(source("s6"))); // $ hasTaintFlow=s6 | ||
| sink(s.escapeHTML(source("s7"))); // $ hasTaintFlow=s7 | ||
| sink(s.unescapeHTML(source("s8"))); // $ hasTaintFlow=s8 | ||
| sink(s.wrap(source("s9"), {})); // $ hasTaintFlow=s9 | ||
| sink(s.dedent(source("s10"), " ")); // $ hasTaintFlow=s10 | ||
| sink(s.reverse(source("s11"))); // $ hasTaintFlow=s11 | ||
| sink(s.pred(source("s12"))); // $ hasTaintFlow=s12 | ||
| sink(s.succ(source("s13"))); // $ hasTaintFlow=s13 | ||
| sink(s.titleize(source("s14"))); // $ hasTaintFlow=s14 | ||
| sink(s.camelize(source("s15"))); // $ hasTaintFlow=s15 | ||
| sink(s.classify(source("s16"))); // $ hasTaintFlow=s16 | ||
| sink(s.underscored(source("s17"))); // $ hasTaintFlow=s17 | ||
| sink(s.dasherize(source("s18"))); // $ hasTaintFlow=s18 | ||
| sink(s.humanize(source("s19"))); // $ hasTaintFlow=s19 | ||
| sink(s.trim(source("s20"),"charsToStrim")); // $ hasTaintFlow=s20 | ||
| sink(s.ltrim(source("s21"),"charsToStrim")); // $ hasTaintFlow=s21 | ||
| sink(s.rtrim(source("s22"),"charsToStrim")); // $ hasTaintFlow=s22 | ||
| sink(s.truncate(source("s23"), 10)); // $ hasTaintFlow=s23 | ||
| sink(s.sprintf(source("s24"), 1.17)); // $ hasTaintFlow=s24 | ||
| sink(s.strRight(source("s25"), "pattern")); // $ hasTaintFlow=s25 | ||
| sink(s.strRightBack(source("s26"), "pattern")); // $ hasTaintFlow=s26 | ||
| sink(s.strLeft(source("s27"), "pattern")); // $ hasTaintFlow=s27 | ||
| sink(s.strLeftBack(source("s28"), "pattern")); // $ hasTaintFlow=s28 | ||
| sink(s.stripTags(source("s29"))); // $ hasTaintFlow=s29 | ||
| sink(s.unquote(source("s30"), "quote")); // $ hasTaintFlow=s30 | ||
| sink(s.map(source("s31"), (x) => {return x;})); // $ hasTaintFlow=s31 | ||
Napalys marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| sink(s.strip(source("s32"),"charsToStrim")); // $ hasTaintFlow=s32 | ||
| sink(s.lstrip(source("s33"),"charsToStrim")); // $ hasTaintFlow=s33 | ||
| sink(s.rstrip(source("s34"),"charsToStrim")); // $ hasTaintFlow=s34 | ||
| sink(s.camelcase(source("s35"))); // $ hasTaintFlow=s35 | ||
| } | ||
|
|
||
| function strToArray() { | ||
| sink(s.chop(source("s1"), 3)); // $ MISSING: hasTaintFlow=s1 | ||
| sink(s.chars(source("s2"))[0]); // $ hasTaintFlow=s2 | ||
| sink(s.words(source("s3"))[0]); // $ hasTaintFlow=s3 | ||
| sink(s.lines(source("s7"))[0]); // $ hasTaintFlow=s7 | ||
| sink(s.chop(source("s1"), 3).length); | ||
| } | ||
|
|
||
| function arrayToStr() { | ||
| sink(s.toSentence([source("s1")])); // $ hasTaintFlow=s1 | ||
| sink(s.toSentenceSerial([source("s2")])); // $ hasTaintFlow=s2 | ||
| } | ||
|
|
||
| function multiSource() { | ||
| sink(s.insert("str", 4, source("s1"))); // $ hasTaintFlow=s1 | ||
| sink(s.insert(source("s2"), 4, "")); // $ hasTaintFlow=s2 | ||
|
|
||
| sink(s.replaceAll("astr", "a", source("s3"))); // $ hasTaintFlow=s3 | ||
| sink(s.replaceAll(source("s4"), "a", "")); // $ hasTaintFlow=s4 | ||
|
|
||
| sink(s.join(",", source("s5"), "str")); // $ hasTaintFlow=s5 | ||
| sink(s.join(",", "str", source("s6"))); // $ hasTaintFlow=s6 | ||
|
|
||
| sink(s.splice(source("s7"), 1, 2, "str")); // $ hasTaintFlow=s7 | ||
| sink(s.splice("str", 1, 2, source("s8"))); // $ hasTaintFlow=s8 | ||
|
|
||
| sink(s.prune(source("s9"), 1, "additional")); // $ hasTaintFlow=s9 | ||
| sink(s.prune("base", 1, source("s10"))); // $ hasTaintFlow=s10 | ||
|
|
||
| sink(s.pad(source("s11"), 10, "charsToPad", "right")); // $ hasTaintFlow=s11 | ||
| sink(s.pad("base", 10, source("s12"), "right")); // $ hasTaintFlow=s12 | ||
|
|
||
| sink(s.lpad(source("s13"), 10, "charsToPad")); // $ hasTaintFlow=s13 | ||
| sink(s.lpad("base", 10, source("s14"))); // $ hasTaintFlow=s14 | ||
|
|
||
| sink(s.rpad(source("s15"), 10, "charsToPad")); // $ hasTaintFlow=s15 | ||
| sink(s.rpad("base", 10, source("s16"))); // $ hasTaintFlow=s16 | ||
|
|
||
| sink(s.repeat(source("s17"), 3, "seperator")); // $ hasTaintFlow=s17 | ||
| sink(s.repeat("base", 3, source("s18"))); // $ hasTaintFlow=s18 | ||
|
|
||
| sink(s.surround(source("s19"), "wrap")); // $ hasTaintFlow=s19 | ||
| sink(s.surround("base", source("s20"))); // $ hasTaintFlow=s20 | ||
|
|
||
| sink(s.quote(source("s21"), "quote")); // $ hasTaintFlow=s21 | ||
| sink(s.quote("base", source("s22"))); // $ hasTaintFlow=s22 | ||
|
|
||
| sink(s.q(source("s23"), "quote")); // $ hasTaintFlow=s23 | ||
| sink(s.q("base", source("s24"))); // $ hasTaintFlow=s24 | ||
|
|
||
| sink(s.rjust(source("s25"), 10, "charsToPad")); // $ hasTaintFlow=s25 | ||
| sink(s.rjust("base", 10, source("s26"))); // $ hasTaintFlow=s26 | ||
|
|
||
| sink(s.ljust(source("s27"), 10, "charsToPad")); // $ hasTaintFlow=s27 | ||
| sink(s.ljust("base", 10, source("s28"))); // $ hasTaintFlow=s28 | ||
| } | ||
|
|
||
| function chaining() { | ||
| sink(s(source("s1")) | ||
| .slugify().capitalize().decapitalize().clean().cleanDiacritics() | ||
| .swapCase().escapeHTML().unescapeHTML().wrap().dedent() | ||
| .reverse().pred().succ().titleize().camelize().classify() | ||
| .underscored().dasherize().humanize().trim().ltrim().rtrim() | ||
| .truncate().sprintf().strRight().strRightBack() | ||
| .strLeft().strLeftBack().stripTags().unquote().value()); // $ hasTaintFlow=s1 | ||
|
|
||
| sink(s(source("s2")) | ||
| .insert(4, source("s3")).replaceAll("a", source("s4")) | ||
| .join(",", source("s5")).splice(1, 2, source("s6")) | ||
| .prune(1, source("s7")).pad(10, source("s8"), "right") | ||
| .lpad(10, source("s9")).rpad(10, source("s10")) | ||
| .repeat(3, source("s11")).surround(source("s12")) | ||
| .quote(source("s13")).value()); // $ hasTaintFlow=s2 hasTaintFlow=s3 hasTaintFlow=s4 hasTaintFlow=s5 hasTaintFlow=s6 hasTaintFlow=s7 hasTaintFlow=s8 hasTaintFlow=s9 hasTaintFlow=s10 hasTaintFlow=s11 hasTaintFlow=s12 hasTaintFlow=s13 | ||
|
|
||
| sink(s(source("s14")).toUpperCase().toLowerCase().replace().slice(1).substring(1).substr(1).concat(source("s15")).split()); // $ hasTaintFlow=s14 hasTaintFlow=s15 | ||
|
|
||
| sink(s(source("s16")) | ||
| .strip().lstrip().rstrip().camelcase() | ||
| .q(source("s17")).ljust(10, source("s18")) | ||
| .rjust(10, source("s19"))); // $ hasTaintFlow=s16 hasTaintFlow=s17 hasTaintFlow=s18 hasTaintFlow=s19 | ||
|
|
||
| sink(s(source("s20")).tap(function(value) { | ||
| return value + source("s21"); | ||
| }).value()); // $ hasTaintFlow=s20 hasTaintFlow=s21 | ||
| } | ||
|
|
||
| function mapTests(){ | ||
| sink(s.map(source("s1"), (x) => {return x + source("s2");})); // $ hasTaintFlow=s1 hasTaintFlow=s2 | ||
| s.map(source("s1"), (x) => { sink(x); return x;}); // $ hasTaintFlow=s1 | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.