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

Skip to content

Commit acef9b7

Browse files
committed
Dynamic/JS: Add library for exporting models
1 parent bd1de17 commit acef9b7

10 files changed

Lines changed: 392 additions & 2 deletions

File tree

javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,11 @@ private predicate isPrivateAssignment(DataFlow::Node node) {
147147
)
148148
}
149149

150-
private predicate isPrivateLike(API::Node node) { isPrivateAssignment(node.asSink()) }
150+
/**
151+
* Holds if `node` is the sink node corresponding to the right-hand side of a private declaration,
152+
* like a private field (`#field`) or class member with the `private` modifier.
153+
*/
154+
predicate isPrivateLike(API::Node node) { isPrivateAssignment(node.asSink()) }
151155

152156
bindingset[name]
153157
private int getNameBadness(string name) {

javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
private import javascript
2020
private import internal.ApiGraphModels as Shared
2121
private import internal.ApiGraphModelsSpecific as Specific
22+
private import semmle.javascript.endpoints.EndpointNaming as EndpointNaming
2223
import Shared::ModelInput as ModelInput
2324
import Shared::ModelOutput as ModelOutput
2425

@@ -55,3 +56,98 @@ private class TaintStepFromSummary extends TaintTracking::SharedTaintStep {
5556
summaryStepNodes(pred, succ, "taint")
5657
}
5758
}
59+
60+
/**
61+
* Specifies which parts of the API graph to export in `ModelExport`.
62+
*/
63+
signature module ModelExportSig {
64+
/**
65+
* Holds if the exported model should contain `node`, if it is publicly accessible.
66+
*
67+
* This ensures that all ways to access `node` will be exported in type models.
68+
*/
69+
predicate shouldContain(API::Node node);
70+
71+
/**
72+
* Holds if a named must be generated for `node` if it is to be included in the exported graph.
73+
*/
74+
default predicate mustBeNamed(API::Node node) { none() }
75+
}
76+
77+
/**
78+
* Module for exporting type models for a given set of nodes in the API graph.
79+
*/
80+
module ModelExport<ModelExportSig S> {
81+
private import codeql.mad.dynamic.GraphExport
82+
83+
private module GraphExportConfig implements GraphExportSig<API::Node> {
84+
predicate edge = Specific::apiGraphHasEdge/3;
85+
86+
predicate shouldContain = S::shouldContain/1;
87+
88+
predicate shouldNotContain(API::Node node) {
89+
EndpointNaming::isPrivateLike(node)
90+
or
91+
node instanceof API::Use
92+
}
93+
94+
predicate mustBeNamed(API::Node node) {
95+
node.getAValueReachingSink() instanceof DataFlow::ClassNode
96+
or
97+
node = API::Internal::getClassInstance(_)
98+
or
99+
S::mustBeNamed(node)
100+
}
101+
102+
predicate exposedName(API::Node node, string type, string path) {
103+
node = API::moduleExport(type) and path = ""
104+
}
105+
106+
predicate suggestedName(API::Node node, string type) {
107+
exists(string package, string name |
108+
(
109+
EndpointNaming::sinkHasPrimaryName(node, package, name) and
110+
not EndpointNaming::aliasDefinition(_, _, _, _, node)
111+
or
112+
EndpointNaming::aliasDefinition(_, _, package, name, node)
113+
) and
114+
type = EndpointNaming::renderName(package, name)
115+
)
116+
}
117+
118+
bindingset[host]
119+
predicate hasTypeSummary(API::Node host, string path) {
120+
exists(string methodName |
121+
functionReturnsReceiver(host.getMember(methodName).getAValueReachingSink()) and
122+
path = "Member[" + methodName + "].ReturnValue"
123+
)
124+
}
125+
126+
pragma[nomagic]
127+
private predicate functionReturnsReceiver(DataFlow::FunctionNode func) {
128+
getAReceiverRef(func).flowsTo(func.getReturnNode())
129+
}
130+
131+
pragma[nomagic]
132+
private DataFlow::MethodCallNode getAReceiverCall(DataFlow::FunctionNode func) {
133+
result = getAReceiverRef(func).getAMethodCall()
134+
}
135+
136+
pragma[nomagic]
137+
private predicate callReturnsReceiver(DataFlow::MethodCallNode call) {
138+
functionReturnsReceiver(call.getACallee().flow())
139+
}
140+
141+
pragma[nomagic]
142+
private DataFlow::SourceNode getAReceiverRef(DataFlow::FunctionNode func) {
143+
result = func.getReceiver()
144+
or
145+
result = getAReceiverCall(func) and
146+
callReturnsReceiver(result)
147+
}
148+
}
149+
150+
private module ExportedGraph = GraphExport<API::Node, GraphExportConfig>;
151+
152+
import ExportedGraph
153+
}

javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ private API::Node getNodeFromType(string type) {
435435
* Gets the API node identified by the first `n` tokens of `path` in the given `(type, path)` tuple.
436436
*/
437437
pragma[nomagic]
438-
private API::Node getNodeFromPath(string type, AccessPath path, int n) {
438+
API::Node getNodeFromPath(string type, AccessPath path, int n) {
439439
isRelevantFullPath(type, path) and
440440
(
441441
n = 0 and

javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,23 @@ module ModelOutputSpecific {
353353
)
354354
}
355355
}
356+
357+
/**
358+
* Holds if the edge `pred -> succ` labelled with `path` exists in the API graph.
359+
*/
360+
bindingset[pred]
361+
predicate apiGraphHasEdge(API::Node pred, string path, API::Node succ) {
362+
exists(string name | succ = pred.getMember(name) and path = "Member[" + name + "]")
363+
or
364+
succ = pred.getUnknownMember() and path = "AnyMember"
365+
or
366+
succ = pred.getInstance() and path = "Instance"
367+
or
368+
succ = pred.getReturn() and path = "ReturnValue"
369+
or
370+
exists(int n | succ = pred.getParameter(n) |
371+
if pred instanceof API::Use then path = "Argument[" + n + "]" else path = "Parameter[" + n + "]"
372+
)
373+
or
374+
succ = pred.getPromised() and path = "Awaited"
375+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
typeModel
2+
| (reexport).func | reexport | Member[func] |
3+
summaryModel
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
extensions:
2+
- addsTo:
3+
pack: codeql/javascript-all
4+
extensible: typeModel
5+
data:
6+
- ["upstream-lib.XYZ", "upstream-lib", "Member[x].Member[y].Member[z]"]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
private import javascript
2+
private import semmle.javascript.endpoints.EndpointNaming as EndpointNaming
3+
private import semmle.javascript.frameworks.data.internal.ApiGraphModels as Shared
4+
5+
module ModelExportConfig implements ModelExportSig {
6+
predicate shouldContain(API::Node node) {
7+
node.getAValueReachingSink() instanceof DataFlow::FunctionNode
8+
}
9+
10+
predicate mustBeNamed(API::Node node) { shouldContain(node) }
11+
}
12+
13+
module Exported = ModelExport<ModelExportConfig>;
14+
15+
query predicate typeModel = Exported::typeModel/3;
16+
17+
query predicate summaryModel = Exported::summaryModel/5;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "reexport",
3+
"main": "reexport.js"
4+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as lib from "upstream-lib";
2+
3+
export { lib };
4+
5+
export const x = lib.x;
6+
export const xy = lib.x.y;
7+
8+
export function func() {
9+
return lib;
10+
}

0 commit comments

Comments
 (0)