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.AnalyzedParameters
private import internal.PreCallGraphStep
private import internal.ModuleInterop
private import semmle.javascript.internal.CachedStages
module DataFlow {

View File

@@ -57,6 +57,10 @@ class TypeTracker extends TTypeTracker {
step = LevelStep() and
result = this
or
step = NoPropStep() and
prop = "" and
result = this
or
exists(string toProp | step = LoadStoreStep(prop, 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
summarizedLoadStoreStep(_, _, fromProp, toProp)
} 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() {
this instanceof LevelStep and result = "level"
or
this instanceof NoPropStep and result = "no-prop"
or
this instanceof LoadAnyProp and result = "load-any"
or
this instanceof CallStep and result = "call"
or
this instanceof ReturnStep and result = "return"

View File

@@ -78,6 +78,9 @@ private module ApiGraphDistance<isRootNodeSig/1 isRootNode, edgeSig/2 edges> {
private predicate distanceFromPackageExport =
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
* in order to name its instance methods.
@@ -96,7 +99,8 @@ private predicate classHasFallbackName(
hasEscapingInstance(cls) and
not exists(distanceFromPackageExport(any(API::Node node | node.getAValueReachingSink() = cls))) and
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())
)
}
@@ -240,7 +244,7 @@ private predicate sinkHasNameCandidate(API::Node sink, string package, string na
*
* `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
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)
@@ -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`.
*/
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. */
@@ -296,7 +312,7 @@ private predicate hasEscapingInstance(DataFlow::ClassNode cls) {
private predicate sourceNodeHasNameCandidate(
DataFlow::SourceNode node, string package, string name, int badness
) {
sinkHasPrimaryName(getASinkNode(node), package, name, badness)
sinkHasPrimaryNameAux(getASinkNode(node), package, name, badness)
or
nameFromGlobal(node, package, name, badness)
}