Compare commits

...

20 Commits

Author SHA1 Message Date
Asger F
a11732ef25 Merge branch 'js/expose-internal-module-naming' into js/vea-hacking 2024-04-25 13:34:14 +02:00
Asger F
a05636876b Merge branch 'js/model-generation-quote' into js/vea-hacking 2024-04-24 10:26:10 +02:00
Asger F
3ba6ddc96e Merge branch 'main' into js/vea-hacking 2024-04-18 11:56:41 +02:00
Asger F
7b3810eb8f Merge branch 'js/endpoint-naming-expose-synthetic' into js/vea-hacking 2024-03-19 14:04:00 +01:00
Asger F
ae903abb4b JS: Expose whether an endpoint name is synthetic 2024-03-19 14:03:33 +01:00
Asger F
3cd4969499 WIP: Add NoPropStep and LoadAnyProp() 2024-03-12 13:01:39 +01:00
Asger F
ba86c93e67 Revert "JS: More aggressive tracking of objects with methods"
This reverts commit 5ed2e033f1.
2024-03-11 15:33:12 +01:00
Asger F
5ed2e033f1 JS: More aggressive tracking of objects with methods 2024-03-11 10:43:15 +01:00
Asger F
ebb744311f Merge branch 'js/call-graph-improvement2' into js/vea-hacking 2024-03-07 12:54:40 +01:00
Asger F
91a0181cfb JS: More implied receiver steps 2024-03-07 12:49:34 +01:00
Asger F
6ebebc131e JS: Add test case 2024-03-07 12:49:10 +01:00
Asger F
f546383cee JS: More implied receiver steps 2024-03-07 11:51:06 +01:00
Asger F
d9482441f0 Merge branch 'js/lift-cg-restriction' into js/vea-hacking 2024-03-06 11:42:55 +01:00
Asger F
941097b639 Update ModuleInterop.qll 2024-03-05 19:09:22 +01:00
Asger F
7ae28ceee0 More Module interop code 2024-03-04 15:46:55 +01:00
Asger F
5340a89107 JS: Remove allocation site restriction in CG 2024-03-01 21:36:29 +01:00
Asger F
c43856d8ea JS: Add steps to better handle module interop code 2024-03-01 21:30:04 +01:00
Asger F
af1382a6ca Merge branch 'js/summarised-tt-store-steps' into js/vea-hacking 2024-03-01 20:26:46 +01:00
Asger F
dc590756b5 Merge branch 'js/escaping-instance-detection' into js/vea-hacking 2024-02-29 11:19:31 +01:00
Asger F
34b48f51de Merge branch 'js/summarised-tt-store-steps' into js/vea-hacking 2024-02-29 10:30:15 +01:00
5 changed files with 114 additions and 5 deletions

View File

@@ -24,6 +24,7 @@ private import internal.FlowSteps as FlowSteps
private import internal.DataFlowNode private import internal.DataFlowNode
private import internal.AnalyzedParameters private import internal.AnalyzedParameters
private import internal.PreCallGraphStep private import internal.PreCallGraphStep
private import internal.ModuleInterop
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages
module DataFlow { module DataFlow {

View File

@@ -57,6 +57,10 @@ class TypeTracker extends TTypeTracker {
step = LevelStep() and step = LevelStep() and
result = this result = this
or or
step = NoPropStep() and
prop = "" and
result = this
or
exists(string toProp | step = LoadStoreStep(prop, toProp) | exists(string toProp | step = LoadStoreStep(prop, toProp) |
result = MkTypeTracker(hasCall, toProp) result = MkTypeTracker(hasCall, toProp)
) )

View File

@@ -0,0 +1,82 @@
private import javascript
private import PreCallGraphStep as PCG
/**
* Base class for calls whose first argument is an imported module.
*/
abstract private class ModuleInteropCall extends DataFlow::CallNode {
private Import getImport() {
this.getArgument(0).getALocalSource() = result.getImportedModuleNode()
}
/** Gets the module that is passed as the first argument. */
Module getImportedModule() { result = this.getImport().getImportedModule() }
/** Holds if the imported module is an ES2015 module or a compiled version thereof. */
predicate isImportingESModule() {
this.getImportedModule() instanceof ES2015Module
or
exists(this.getImportedModule().getAnExportedValue("__esModule"))
}
abstract predicate step(DataFlow::Node pred, DataFlow::Node succ);
}
/** A call that wraps the imported object in a `default` property, unless it is from an ES2015 module. */
private class InteropRequireCall extends ModuleInteropCall {
InteropRequireCall() {
this.getCalleeName() = ["_interopRequireDefault", "_interopRequireWildcard"]
}
DataFlow::SourceNode getImportedObjectRef() {
this.isImportingESModule() and
result = this
or
(not this.isImportingESModule() or this.getCalleeName() = "_interopRequireWildcard") and
result = this.getAPropertyRead("default")
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = this.getArgument(0) and
succ = this.getImportedObjectRef()
or
exists(string prop |
pred = this.getImportedModule().getAnExportedValue(prop) and
succ = this.getImportedObjectRef().getAPropertyRead(prop)
)
}
}
/**
* A call that read the `default` property if it exists, otherwise returns the object as-is.
*/
private class InteropDefaultCall extends ModuleInteropCall {
InteropDefaultCall() { this.getCalleeName() = "_interopDefault" }
DataFlow::Node getDefaultExport() {
result = this.getImportedModule().getAnExportedValue("default")
}
DataFlow::Node getImportedObjectSource() {
result = this.getDefaultExport()
or
not exists(this.getDefaultExport()) and
result = this.getArgument(0)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = this.getImportedObjectSource() and
succ = this
or
exists(string prop |
pred = this.getDefaultExport().getALocalSource().getAPropertyWrite(prop).getRhs() and
succ = this.getAPropertyRead(prop)
)
}
}
private class Step extends PCG::PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
any(ModuleInteropCall call).step(pred, succ)
}
}

View File

@@ -48,7 +48,9 @@ private module Cached {
or or
summarizedLoadStoreStep(_, _, fromProp, toProp) summarizedLoadStoreStep(_, _, fromProp, toProp)
} or } or
WithoutPropStep(PropertySet props) { SharedTypeTrackingStep::withoutPropStep(_, _, props) } WithoutPropStep(PropertySet props) { SharedTypeTrackingStep::withoutPropStep(_, _, props) } or
NoPropStep() or
LoadAnyProp()
} }
/** /**
@@ -224,6 +226,10 @@ class StepSummary extends TStepSummary {
string toString() { string toString() {
this instanceof LevelStep and result = "level" this instanceof LevelStep and result = "level"
or or
this instanceof NoPropStep and result = "no-prop"
or
this instanceof LoadAnyProp and result = "load-any"
or
this instanceof CallStep and result = "call" this instanceof CallStep and result = "call"
or or
this instanceof ReturnStep and result = "return" this instanceof ReturnStep and result = "return"

View File

@@ -78,6 +78,9 @@ private module ApiGraphDistance<isRootNodeSig/1 isRootNode, edgeSig/2 edges> {
private predicate distanceFromPackageExport = private predicate distanceFromPackageExport =
ApiGraphDistance<isPackageExport/1, relevantEdge/2>::distanceTo/1; ApiGraphDistance<isPackageExport/1, relevantEdge/2>::distanceTo/1;
/** Badness penalty to apply when synthesizing a name. */
private int syntheticNameBadness() { result = 100 }
/** /**
* Holds if `(package, name)` is the fallback name for `cls`, to be used as a last resort * Holds if `(package, name)` is the fallback name for `cls`, to be used as a last resort
* in order to name its instance methods. * in order to name its instance methods.
@@ -96,7 +99,8 @@ private predicate classHasFallbackName(
hasEscapingInstance(cls) and hasEscapingInstance(cls) and
not exists(distanceFromPackageExport(any(API::Node node | node.getAValueReachingSink() = cls))) and not exists(distanceFromPackageExport(any(API::Node node | node.getAValueReachingSink() = cls))) and
exists(string baseName | exists(string baseName |
InternalModuleNaming::fallbackModuleName(cls.getTopLevel(), package, baseName, badness - 100) and InternalModuleNaming::fallbackModuleName(cls.getTopLevel(), package, baseName,
badness - syntheticNameBadness()) and
name = join(baseName, cls.getName()) name = join(baseName, cls.getName())
) )
} }
@@ -240,7 +244,7 @@ private predicate sinkHasNameCandidate(API::Node sink, string package, string na
* *
* `badness` is bound to the associated badness of the name. * `badness` is bound to the associated badness of the name.
*/ */
private predicate sinkHasPrimaryName(API::Node sink, string package, string name, int badness) { private predicate sinkHasPrimaryNameAux(API::Node sink, string package, string name, int badness) {
badness = min(int b | sinkHasNameCandidate(sink, _, _, b) | b) and badness = min(int b | sinkHasNameCandidate(sink, _, _, b) | b) and
package = min(string p | sinkHasNameCandidate(sink, p, _, badness) | p) and package = min(string p | sinkHasNameCandidate(sink, p, _, badness) | p) and
name = min(string n | sinkHasNameCandidate(sink, package, n, badness) | n order by n.length(), n) name = min(string n | sinkHasNameCandidate(sink, package, n, badness) | n order by n.length(), n)
@@ -250,7 +254,19 @@ private predicate sinkHasPrimaryName(API::Node sink, string package, string name
* Holds if `(package, name)` is the primary name to associate with `node`. * Holds if `(package, name)` is the primary name to associate with `node`.
*/ */
predicate sinkHasPrimaryName(API::Node sink, string package, string name) { predicate sinkHasPrimaryName(API::Node sink, string package, string name) {
sinkHasPrimaryName(sink, package, name, _) sinkHasPrimaryNameAux(sink, package, name, _)
}
/**
* Holds if `(package, name)` is the primary name to associate with `node`.
*
* `isSynthetic` is bound to true if the name was synthesized, and false if it is a proper access path.
*/
predicate sinkHasPrimaryName(API::Node sink, string package, string name, boolean isSynthetic) {
exists(int badness |
sinkHasPrimaryNameAux(sink, package, name, badness) and
(if badness >= syntheticNameBadness() then isSynthetic = true else isSynthetic = false)
)
} }
/** Gets a source node that can flow to `sink` without using a return step. */ /** Gets a source node that can flow to `sink` without using a return step. */
@@ -296,7 +312,7 @@ private predicate hasEscapingInstance(DataFlow::ClassNode cls) {
private predicate sourceNodeHasNameCandidate( private predicate sourceNodeHasNameCandidate(
DataFlow::SourceNode node, string package, string name, int badness DataFlow::SourceNode node, string package, string name, int badness
) { ) {
sinkHasPrimaryName(getASinkNode(node), package, name, badness) sinkHasPrimaryNameAux(getASinkNode(node), package, name, badness)
or or
nameFromGlobal(node, package, name, badness) nameFromGlobal(node, package, name, badness)
} }