Skip to content

Commit cea435c

Browse files
authored
Merge pull request #178 from microsoft/powershell-ipa-the-ast
PS: Simplify the AST in Powershell
2 parents 4c13821 + 0fc5778 commit cea435c

File tree

255 files changed

+8646
-4644
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

255 files changed

+8646
-4644
lines changed

powershell/codeql-extractor.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ file_types:
1010
display_name: powershellscripts
1111
extensions:
1212
- .ps1
13-
- .psd1
13+
- .psd1
14+
- .psm1

powershell/extractor/Semmle.Extraction.PowerShell.Standalone/Options.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public override void InvalidArgument(string argument)
7474
/// <summary>
7575
/// List of extensions to include.
7676
/// </summary>
77-
public IList<string> Extensions { get; } = new List<string>() { ".ps1" };
77+
public IList<string> Extensions { get; } = new List<string>() { ".ps1", ".psd1", ".psm1" };
7878

7979
/// <summary>
8080
/// Files/patterns to exclude.

powershell/extractor/Semmle.Extraction/Tuples.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal static void folders(this System.IO.TextWriter trapFile, Folder folder,
3030

3131
public static void locations_default(this System.IO.TextWriter trapFile, SourceLocation label, Entities.File file, int startLine, int startCol, int endLine, int endCol)
3232
{
33-
trapFile.WriteTuple("locations_default", label, file, startLine, startCol, endLine, endCol);
33+
trapFile.WriteTuple("locations_default", label, file, startLine, startCol, endLine, endCol - 1);
3434
}
3535
}
3636
}

powershell/ql/lib/powershell.qll

Lines changed: 1 addition & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1 @@
1-
import semmle.code.powershell.File
2-
import semmle.code.powershell.Location
3-
import semmle.code.powershell.SourceLocation
4-
import semmle.code.powershell.Ast
5-
import semmle.code.powershell.Statement
6-
import semmle.code.powershell.Expression
7-
import semmle.code.powershell.CommandBase
8-
import semmle.code.powershell.AttributeBase
9-
import semmle.code.powershell.PipelineBase
10-
import semmle.code.powershell.PipelineChain
11-
import semmle.code.powershell.BaseConstantExpression
12-
import semmle.code.powershell.ConstantExpression
13-
import semmle.code.powershell.MemberExpressionBase
14-
import semmle.code.powershell.Attribute
15-
import semmle.code.powershell.NamedAttributeArgument
16-
import semmle.code.powershell.TypeConstraint
17-
import semmle.code.powershell.VariableExpression
18-
import semmle.code.powershell.ModuleSpecification
19-
import semmle.code.powershell.ParamBlock
20-
import semmle.code.powershell.NamedBlock
21-
import semmle.code.powershell.ScriptBlock
22-
import semmle.code.powershell.StringLiteral
23-
import semmle.code.powershell.AssignmentStatement
24-
import semmle.code.powershell.BinaryExpression
25-
import semmle.code.powershell.UnaryExpression
26-
import semmle.code.powershell.ScriptBlockExpr
27-
import semmle.code.powershell.TernaryExpression
28-
import semmle.code.powershell.UsingExpression
29-
import semmle.code.powershell.TrapStatement
30-
import semmle.code.powershell.StatementBlock
31-
import semmle.code.powershell.ArrayExpression
32-
import semmle.code.powershell.ArrayLiteral
33-
import semmle.code.powershell.CommandElement
34-
import semmle.code.powershell.Redirection
35-
import semmle.code.powershell.FileRedirection
36-
import semmle.code.powershell.MergingRedirection
37-
import semmle.code.powershell.LoopStmt
38-
import semmle.code.powershell.DoWhileStmt
39-
import semmle.code.powershell.DoUntilStmt
40-
import semmle.code.powershell.WhileStmt
41-
import semmle.code.powershell.ForStmt
42-
import semmle.code.powershell.ForEachStmt
43-
import semmle.code.powershell.GotoStmt
44-
import semmle.code.powershell.ContinueStmt
45-
import semmle.code.powershell.BreakStmt
46-
import semmle.code.powershell.ReturnStmt
47-
import semmle.code.powershell.UsingStmt
48-
import semmle.code.powershell.ThrowStmt
49-
import semmle.code.powershell.ErrorStmt
50-
import semmle.code.powershell.Type
51-
import semmle.code.powershell.Member
52-
import semmle.code.powershell.PropertyMember
53-
import semmle.code.powershell.Function
54-
import semmle.code.powershell.TryStmt
55-
import semmle.code.powershell.IfStmt
56-
import semmle.code.powershell.SwitchStmt
57-
import semmle.code.powershell.ExitStmt
58-
import semmle.code.powershell.LabeledStmt
59-
import semmle.code.powershell.DynamicStmt
60-
import semmle.code.powershell.DataStmt
61-
import semmle.code.powershell.Configuration
62-
import semmle.code.powershell.CatchClause
63-
import semmle.code.powershell.Command
64-
import semmle.code.powershell.CommandExpression
65-
import semmle.code.powershell.CommandParameter
66-
import semmle.code.powershell.ExpandableStringExpression
67-
import semmle.code.powershell.TypeExpression
68-
import semmle.code.powershell.ParenExpression
69-
import semmle.code.powershell.Chainable
70-
import semmle.code.powershell.Pipeline
71-
import semmle.code.powershell.StringConstantExpression
72-
import semmle.code.powershell.MemberExpr
73-
import semmle.code.powershell.InvokeMemberExpression
74-
import semmle.code.powershell.Call
75-
import semmle.code.powershell.ObjectCreation
76-
import semmle.code.powershell.SubExpression
77-
import semmle.code.powershell.ErrorExpr
78-
import semmle.code.powershell.ConvertExpr
79-
import semmle.code.powershell.IndexExpr
80-
import semmle.code.powershell.HashTable
81-
import semmle.code.powershell.SplitExpr
82-
import semmle.code.powershell.CommentEntity
83-
import semmle.code.powershell.Variable
84-
import semmle.code.powershell.internal.Internal::Public
85-
import semmle.code.powershell.ModuleManifest
1+
import semmle.code.powershell.ast.Ast

powershell/ql/lib/semmle/code/powershell/ApiGraphs.qll

Lines changed: 66 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ module API {
271271
this = Impl::MkMethodAccessNode(result) or
272272
this = Impl::MkBackwardNode(result, _) or
273273
this = Impl::MkForwardNode(result, _) or
274-
this = Impl::MkSinkNode(result)
274+
this = Impl::MkSinkNode(result) or
275+
this = Impl::MkNamespaceOfTypeNameNode(result)
275276
}
276277

277278
/** Gets the location of this node. */
@@ -324,45 +325,6 @@ module API {
324325
}
325326
}
326327

327-
/** A node representing a module/class object with epsilon edges to its descendents. */
328-
private class ModuleNode extends Node, Impl::MkModule {
329-
string qualifiedModule;
330-
int n;
331-
332-
ModuleNode() { this = Impl::MkModule(qualifiedModule, n) }
333-
334-
ModuleNode getNext() { result = Impl::MkModule(qualifiedModule, n + 1) }
335-
336-
ModuleNode getPred() { result.getNext() = this }
337-
338-
string getComponent() { result = qualifiedModule.splitAt(".", n) }
339-
340-
string getModule() {
341-
not exists(this.getPred()) and
342-
result = this.getComponent()
343-
or
344-
result = this.getPred().getModule() + "." + this.getComponent()
345-
}
346-
347-
override string toString() { result = "Module(" + this.getModule() + ")" }
348-
}
349-
350-
/** A node representing instances of a module/class with epsilon edges to its ancestors. */
351-
private class InstanceUp extends Node, Impl::MkInstanceUp {
352-
/** Gets the module whose instances are represented by this API node. */
353-
string getType() { this = Impl::MkInstanceUp(result) }
354-
355-
override string toString() { result = "ModuleInstanceUp(" + this.getType() + ")" }
356-
}
357-
358-
/** A node representing instances of a module/class with epsilon edges to its descendents. */
359-
private class InstanceDownNode extends Node, Impl::MkInstanceDown {
360-
/** Gets the module whose instances are represented by this API node. */
361-
string getType() { this = Impl::MkInstanceDown(result) }
362-
363-
override string toString() { result = "ModuleInstanceDown(" + this.getType() + ")" }
364-
}
365-
366328
/** A node corresponding to the method being invoked at a method call. */
367329
class MethodAccessNode extends Node, Impl::MkMethodAccessNode {
368330
override string toString() { result = "MethodAccessNode(" + this.asCall() + ")" }
@@ -378,6 +340,22 @@ module API {
378340
override string toString() { result = "SinkNode(" + this.getInducingNode() + ")" }
379341
}
380342

343+
private class UsingNode extends Node, Impl::MkUsingNode {
344+
UsingStmt using; // TODO: This should really be the cfg node, I think
345+
346+
UsingNode() { this = Impl::MkUsingNode(using) }
347+
348+
override string toString() { result = "UsingNode(" + using + ")" }
349+
}
350+
351+
private class NamespaceOfTypeNameNode extends Node, Impl::MkNamespaceOfTypeNameNode {
352+
DataFlow::QualifiedTypeNameNode typeName;
353+
354+
NamespaceOfTypeNameNode() { this = Impl::MkNamespaceOfTypeNameNode(typeName) }
355+
356+
override string toString() { result = "NamespaceOfTypeNameNode(" + typeName + ")" }
357+
}
358+
381359
/**
382360
* An API entry point.
383361
*
@@ -415,11 +393,15 @@ module API {
415393
/** Gets the root node. */
416394
Node root() { result instanceof RootNode }
417395

418-
/**
419-
* Gets the node that represents the module with qualified
420-
* name `qualifiedModule`.
421-
*/
422-
ModuleNode mod(string qualifiedModule, int n) { result = Impl::MkModule(qualifiedModule, n) }
396+
bindingset[name]
397+
pragma[inline_late]
398+
Node namespace(string name) {
399+
// This predicate is currently not 'inline_late' because 'n' can be an input or output
400+
Impl::namespace(name, result)
401+
}
402+
403+
pragma[inline]
404+
Node getTopLevelMember(string name) { Impl::topLevelMember(name, result) }
423405

424406
/**
425407
* Gets an unqualified call at the top-level with the given method name.
@@ -466,44 +448,14 @@ module API {
466448

467449
cached
468450
private module Impl {
469-
private predicate isGacModule(string s) {
470-
s =
471-
[
472-
"System.Management.Automation",
473-
"Microsoft.Management.Infrastructure",
474-
"Microsoft.PowerShell.Security",
475-
"Microsoft.PowerShell.Commands.Management",
476-
"Microsoft.PowerShell.Commands.Utility"
477-
]
478-
}
479-
480-
private predicate isModule(string s, int n) {
481-
(
482-
any(UsingStmt using).getName() = s
483-
or
484-
any(Cmd cmd).getNamespaceQualifier() = s
485-
or
486-
any(TypeNameExpr tn).getName() = s
487-
or
488-
any(ModuleManifest manifest).getModuleName() = s
489-
or
490-
isGacModule(s)
491-
) and
492-
exists(s.splitAt(".", n))
493-
}
494-
495451
cached
496452
newtype TApiNode =
497453
/** The root of the API graph. */
498454
MkRoot() or
499455
/** The method accessed at `call`, synthetically treated as a separate object. */
500456
MkMethodAccessNode(DataFlow::CallNode call) or
501-
MkModule(string qualifiedModule, int n) { isModule(qualifiedModule, n) } or
502-
/** Instances of `mod` with epsilon edges to its ancestors. */
503-
MkInstanceUp(string qualifiedType) { exists(MkModule(qualifiedType, _)) } or
504-
/** Instances of `mod` with epsilon edges to its descendents, and to its upward node. */
505-
MkInstanceDown(string qualifiedType) { exists(MkModule(qualifiedType, _)) } or
506-
/** Intermediate node for following forward data flow. */
457+
MkUsingNode(UsingStmt using) or
458+
MkNamespaceOfTypeNameNode(DataFlow::QualifiedTypeNameNode typeName) or
507459
MkForwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or
508460
/** Intermediate node for following backward data flow. */
509461
MkBackwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or
@@ -523,10 +475,30 @@ module API {
523475
pragma[inline_late]
524476
private DataFlow::Node getNodeFromExpr(Expr e) { result.asExpr().getExpr() = e }
525477

478+
private import frameworks.data.ModelsAsData
479+
480+
cached
481+
predicate namespace(string name, Node node) {
482+
exists(DataFlow::QualifiedTypeNameNode typeName |
483+
typeName.getNamespace() = name and
484+
node = MkNamespaceOfTypeNameNode(typeName)
485+
)
486+
or
487+
exists(UsingStmt using |
488+
using.getName().toLowerCase() = name and
489+
node = MkUsingNode(using)
490+
)
491+
or
492+
node = ModelOutput::getATypeNode(name)
493+
}
494+
495+
cached
496+
predicate topLevelMember(string name, Node node) { memberEdge(root(), name, node) }
497+
526498
cached
527499
predicate toplevelCall(string name, Node node) {
528500
exists(DataFlow::CallNode call |
529-
call.asExpr().getExpr().getEnclosingScope() instanceof TopLevel and
501+
call.asExpr().getExpr().getEnclosingScope() instanceof TopLevelScriptBlock and
530502
call.getName() = name and
531503
node = MkMethodAccessNode(call)
532504
)
@@ -544,26 +516,25 @@ module API {
544516

545517
cached
546518
predicate memberEdge(Node pred, string name, Node succ) {
547-
exists(MemberExpr member | succ = getForwardStartNode(getNodeFromExpr(member)) |
548-
pred = getForwardEndNode(getALocalSourceStrict(getNodeFromExpr(member.getQualifier()))) and
549-
name = member.getMemberName()
519+
exists(StringConstExpr read |
520+
succ = getForwardStartNode(getNodeFromExpr(read)) and
521+
pred = MkRoot() and
522+
name = read.getValueString()
523+
)
524+
or
525+
exists(DataFlow::QualifiedTypeNameNode typeName |
526+
typeName.getName() = name and
527+
pred = MkNamespaceOfTypeNameNode(typeName) and
528+
succ = getForwardStartNode(typeName)
550529
)
530+
// or
531+
// TODO: Handle getAMember when the predecessor is a MkUsingNode?
551532
}
552533

553534
cached
554535
predicate methodEdge(Node pred, string name, Node succ) {
555536
exists(DataFlow::CallNode call | succ = MkMethodAccessNode(call) and name = call.getName() |
556537
pred = getForwardEndNode(getALocalSourceStrict(call.getQualifier()))
557-
or
558-
exists(string qualifiedModule, ModuleManifest manifest, int n |
559-
pred = mod(qualifiedModule, n) and
560-
not exists(mod(qualifiedModule, n + 1)) and
561-
manifest.getModuleName() = qualifiedModule
562-
|
563-
manifest.getACmdLetToExport() = name
564-
or
565-
manifest.getAFunctionToExport() = name
566-
)
567538
)
568539
}
569540

@@ -657,24 +628,10 @@ module API {
657628

658629
cached
659630
predicate instanceEdge(Node pred, Node succ) {
660-
exists(string qualifiedType, int n |
661-
pred = MkModule(qualifiedType, n) and
662-
not exists(MkModule(qualifiedType, n + 1))
663-
|
664-
exists(DataFlow::TypeNameNode typeName |
665-
typeName.getTypeName() = qualifiedType and
666-
succ = getForwardStartNode(typeName)
667-
)
668-
or
669-
exists(DataFlow::ObjectCreationNode objCreation |
670-
objCreation.getConstructedTypeName() = qualifiedType and
671-
succ = getForwardStartNode(objCreation)
672-
)
673-
or
674-
exists(DataFlow::ParameterNode p |
675-
p.getParameter().getStaticType() = qualifiedType and
676-
succ = getForwardStartNode(p)
677-
)
631+
// TODO: Also model parameters with a given type here
632+
exists(DataFlow::ObjectCreationNode objCreation |
633+
pred = getForwardEndNode(objCreation.getConstructedTypeNode()) and
634+
succ = getForwardStartNode(objCreation)
678635
)
679636
}
680637

powershell/ql/lib/semmle/code/powershell/ArrayExpression.qll

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)