Skip to content

Commit 43f986c

Browse files
authored
Merge pull request #132 from microsoft/powershell-extract-implicit-models
PS: Extract source files found via `PSModulePath`
2 parents e6f8df7 + a56cbfe commit 43f986c

File tree

12 files changed

+6677
-7
lines changed

12 files changed

+6677
-7
lines changed

powershell/downgrades/c5191f89a6e3e8cea428b5c7326a06e335738533/old.dbscheme

Lines changed: 1652 additions & 0 deletions
Large diffs are not rendered by default.

powershell/downgrades/c5191f89a6e3e8cea428b5c7326a06e335738533/semmlecode.powershell.dbscheme

Lines changed: 1648 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
description: Add is_in_psmodule_path relation
2+
compatibility: full
3+
is_in_psmodule_path.rel: delete

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ public override bool HandleFlag(string key, bool value)
2424
case "dry-run":
2525
SkipExtraction = value;
2626
return true;
27+
case "skip-psmodulepath-files":
28+
SkipPSModulePathFiles = value;
29+
return true;
2730
default:
2831
return base.HandleFlag(key, value);
2932
}
@@ -74,7 +77,7 @@ public override void InvalidArgument(string argument)
7477
/// <summary>
7578
/// List of extensions to include.
7679
/// </summary>
77-
public IList<string> Extensions { get; } = new List<string>() { ".ps1" };
80+
public IList<string> Extensions { get; } = new List<string>() { ".ps1", ".psd1" };
7881

7982
/// <summary>
8083
/// Files/patterns to exclude.
@@ -127,6 +130,12 @@ private static FileInfo[] GetDefaultFiles()
127130
/// </summary>
128131
public bool SkipExtraction { get; private set; } = false;
129132

133+
/// <summary>
134+
/// Whether to extract files in the paths found in the `PSModulePath`
135+
/// environment variable.
136+
/// </summary>
137+
public bool SkipPSModulePathFiles { get; private set; } = false;
138+
130139
/// <summary>
131140
/// Whether errors were encountered parsing the arguments.
132141
/// </summary>
@@ -158,13 +167,14 @@ public static void ShowHelp(System.IO.TextWriter output)
158167
"PowerShell# standalone extractor\n\nExtracts PowerShell scripts in the current directory.\n"
159168
);
160169
output.WriteLine("Additional options:\n");
161-
output.WriteLine(" <path> Use the provided path instead.");
170+
output.WriteLine(" <path> Use the provided path instead.");
162171
output.WriteLine(
163-
" --exclude:xxx Exclude a file or directory (can be specified multiple times)"
172+
" --exclude:xxx Exclude a file or directory (can be specified multiple times)"
164173
);
165-
output.WriteLine(" --dry-run Stop before extraction");
166-
output.WriteLine(" --threads:nnn Specify number of threads (default=CPU cores)");
167-
output.WriteLine(" --verbose Produce more output");
174+
output.WriteLine(" --dry-run Stop before extraction");
175+
output.WriteLine(" --threads:nnn Specify number of threads (default=CPU cores)");
176+
output.WriteLine(" --verbose Produce more output");
177+
output.WriteLine(" --skip-psmodulepath-files Avoid extracting source files in paths specified by the PSModulePath environment variable.");
168178
}
169179

170180
private Options() { }

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public static int Main(string[] args)
4545

4646
output.Log(Severity.Info, "Running PowerShell standalone extractor");
4747
var sourceFiles = options
48-
.Files.Where(d =>
48+
.Files.Concat(GetPSModuleFiles(options))
49+
.Where(d =>
4950
options.Extensions.Contains(
5051
d.Extension,
5152
StringComparer.InvariantCultureIgnoreCase
@@ -87,6 +88,30 @@ public static int Main(string[] args)
8788
return 0;
8889
}
8990

91+
private static String[] GetPSModulePaths()
92+
{
93+
return Environment.GetEnvironmentVariable("PSModulePath")?.Split(Path.PathSeparator)
94+
?? Array.Empty<string>();
95+
}
96+
97+
private static IEnumerable<FileInfo> GetPSModuleFiles(Options options)
98+
{
99+
if(options.SkipPSModulePathFiles)
100+
{
101+
return Array.Empty<FileInfo>();
102+
}
103+
104+
return GetPSModulePaths()
105+
.Where(d => Directory.Exists(d))
106+
.SelectMany(d =>
107+
{
108+
var di = new DirectoryInfo(d);
109+
return di.Exists
110+
? di.GetFiles("*.*", SearchOption.AllDirectories)
111+
: new FileInfo[] { new(d) };
112+
});
113+
}
114+
90115
private class ExtractionProgress : IProgressMonitor
91116
{
92117
public ExtractionProgress(ILogger output)

powershell/extractor/Semmle.Extraction.PowerShell/Entities/Base/File.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System;
33
using System.Collections.Generic;
44
using System.IO;
5+
using System.Linq;
56

67
namespace Semmle.Extraction.PowerShell.Entities
78
{
@@ -14,6 +15,18 @@ protected File(PowerShellContext cx, string path)
1415
{
1516
}
1617

18+
private static string[] GetPSModulePaths()
19+
{
20+
return Environment.GetEnvironmentVariable("PSModulePath")?.Split(Path.PathSeparator)
21+
?? Array.Empty<string>();
22+
}
23+
24+
private bool PathIsInPSModulePath()
25+
{
26+
// Check if f's path is inside one of the paths in $Env:PSModulePath
27+
return GetPSModulePaths().Any(originalPath.StartsWith);
28+
}
29+
1730
public override void Populate(TextWriter trapFile)
1831
{
1932
trapFile.files(this, TransformedPath.Value);
@@ -23,6 +36,11 @@ public override void Populate(TextWriter trapFile)
2336
trapFile.containerparent(Extraction.Entities.Folder.Create(PowerShellContext, dir), this);
2437
}
2538

39+
if(PathIsInPSModulePath())
40+
{
41+
trapFile.is_in_psmodule_path(this);
42+
}
43+
2644
try
2745
{
2846
System.Text.Encoding encoding;

powershell/extractor/Semmle.Extraction/Tuples.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,9 @@ public static void locations_default(this System.IO.TextWriter trapFile, SourceL
3232
{
3333
trapFile.WriteTuple("locations_default", label, file, startLine, startCol, endLine, endCol);
3434
}
35+
36+
public static void is_in_psmodule_path(this System.IO.TextWriter trapFile, Entities.File file) {
37+
trapFile.WriteTuple("is_in_psmodule_path", file);
38+
}
3539
}
3640
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ class File extends Container, @file {
209209
not this.isStub()
210210
}
211211

212+
predicate isInPSModulePath() {
213+
is_in_psmodule_path(this)
214+
}
215+
212216
/** Holds if this file is a library. */
213217
predicate fromLibrary() {
214218
not this.getBaseName() = "" and

powershell/ql/lib/semmlecode.powershell.dbscheme

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ containerparent(
4141
unique int child: @container ref
4242
);
4343

44+
is_in_psmodule_path(
45+
int file: @file ref
46+
);
47+
4448
/* Comments */
4549
comment_entity(
4650
unique int id: @comment_entity,

0 commit comments

Comments
 (0)