Skip to content

Commit fbbb13a

Browse files
authored
Merge pull request #330 from microsoft/chanel-fixes
PS: Two bugfixes reported by Chanel
2 parents 25df927 + 2b4ac31 commit fbbb13a

File tree

10 files changed

+2639
-3
lines changed

10 files changed

+2639
-3
lines changed

powershell/misc/GenerateFromDB.ql

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* This query generates type models for PowerShell from a C# database.
3+
*
4+
* It is not meant to be run manually. Instead, it is executed
5+
* by `typemodelgenFromDB.py`.
6+
*/
7+
import csharp
8+
private import semmle.code.csharp.commons.QualifiedName
9+
10+
private module FullyQualifiedNameInput implements QualifiedNameInputSig {
11+
string getUnboundGenericSuffix(UnboundGeneric ug) { exists(ug) and result = "" }
12+
}
13+
14+
predicate hasFullyQualifiedNameImpl(Declaration d, string namespace, string type, string name) {
15+
QualifiedName<FullyQualifiedNameInput>::hasQualifiedName(d, namespace, type, name)
16+
}
17+
18+
predicate hasFullyQualifiedNameImpl(Declaration d, string qualifier, string name) {
19+
QualifiedName<FullyQualifiedNameInput>::hasQualifiedName(d, qualifier, name)
20+
}
21+
22+
predicate hasFullyQualifiedName(Callable callable, string namespace, string type, string name) {
23+
hasFullyQualifiedNameImpl(callable.(Method).getUnboundDeclaration(), namespace, type, name)
24+
}
25+
26+
Type getReturnType(Callable c) {
27+
result = c.(Method).getReturnType()
28+
}
29+
30+
bindingset[t]
31+
Type remap(Type t) {
32+
if hasFullyQualifiedNameImpl(t, "System", "ReadOnlySpan")
33+
then hasFullyQualifiedNameImpl(result, "System", "String")
34+
else (
35+
if t instanceof TupleType
36+
then hasFullyQualifiedNameImpl(result, "System", "ValueTuple")
37+
else result = t
38+
)
39+
}
40+
41+
from Callable m, Type t, string namespace, string type, string name, string qualifier, string return
42+
where
43+
hasFullyQualifiedName(m, namespace, type, name) and
44+
not type.matches("%+%") and
45+
not return.matches("%+%") and
46+
getReturnType(m) = t and
47+
not t instanceof VoidType and
48+
hasFullyQualifiedNameImpl(remap(t.getUnboundDeclaration()), qualifier, return) and
49+
qualifier != "" and
50+
m.fromSource() and
51+
exists(string relative |
52+
relative = m.getLocation().getFile().getRelativePath() and
53+
not relative.toLowerCase().matches("%/tests/%")
54+
)
55+
select qualifier.toLowerCase() + "." + return.toLowerCase(),
56+
namespace.toLowerCase() + "." + type.toLowerCase(), name.toLowerCase()

powershell/misc/README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Type information about .NET and PowerShell SDK methods are obtained by generatin
55

66
The type models are located here: https://github.com/microsoft/codeql/blob/main/powershell/ql/lib/semmle/code/powershell/frameworks/
77

8+
## dotnet/dotnet-api-docs and MicrosoftDocs/powershell-docs-sdk-dotnet
9+
810
Follow the steps below in order to generate new type models:
911
1. Join the `MicrosoftDocs` organisation to ensure that you can access https://github.com/MicrosoftDocs/powershell-docs-sdk-dotnet/tree/main/dotnet/xml (if you haven't already).
1012
2.
@@ -20,4 +22,13 @@ Run the following commands
2022
```
2123
This will generate 600+ folders that need to be copied into https://github.com/microsoft/codeql/blob/main/powershell/ql/lib/semmle/code/powershell/frameworks/.
2224

23-
Note: Care must be taken to ensure that manually modified versions aren't overwritten.
25+
## dotnet/SqlClient
26+
27+
The type models for this is generated via a CodeQL query. Following these steps to generate these type models
28+
29+
1. Download a C# DB for `dotnet/SqlClient` (for instance using VSCode's `CodeQL: Download Database from GitHub`)
30+
2. Run the following command (note: your current working directory is assumed to be CodeQL):
31+
```
32+
python .\powershell\misc\typemodelgenFromDB.py . powershell/ql/lib/semmle/code/powershell/frameworks/generated/dotnet.sqlclient.typemodel.yml path/to/the/db/you/downloaded/above
33+
```
34+
This will generate the file `dotnet.sqlclient.typemodel.yml` inside the right folder.

powershell/misc/qlpack.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: microsoft-sdl/powershell-typegen
2+
version: 0.0.1
3+
extractor: csharp
4+
dependencies:
5+
codeql/csharp-all: "*"
6+
warnOnImplicitThis: true
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""Generate a typemodel.yml file from a CodeQL BQRS query result."""
2+
3+
import argparse
4+
import csv
5+
import subprocess
6+
import sys
7+
import tempfile
8+
from pathlib import Path
9+
10+
HEADER = """\
11+
# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
12+
extensions:
13+
- addsTo:
14+
pack: microsoft/powershell-all
15+
extensible: typeModel
16+
data:
17+
"""
18+
19+
20+
def generate_type_models(output: str, codeql: str, db: str) -> None:
21+
"""Decode a BQRS file to CSV and convert it into a typemodel YAML file."""
22+
with tempfile.TemporaryDirectory() as tmpdir:
23+
bqrs_path = Path(tmpdir) / "out.bqrs"
24+
query_path = Path(codeql) / "powershell" / "misc" / "GenerateFromDB.ql"
25+
subprocess.run(
26+
[
27+
"codeql", "query", "run", str(query_path),
28+
"--additional-packs", str(codeql),
29+
"-d", str(db),
30+
"-o", str(bqrs_path),
31+
],
32+
check=True,
33+
)
34+
35+
csv_path = Path(tmpdir) / "results.csv"
36+
subprocess.run(
37+
[
38+
"codeql", "bqrs", "decode",
39+
str(bqrs_path),
40+
"--no-titles",
41+
"--format=csv",
42+
"-o", str(csv_path),
43+
],
44+
check=True,
45+
)
46+
47+
rows = _read_csv(csv_path)
48+
49+
output_path = Path(output)
50+
output_path.parent.mkdir(parents=True, exist_ok=True)
51+
52+
lines = [
53+
f' - ["{t}", "{d}", "Method[{m}].ReturnValue"]'
54+
for t, d, m in rows
55+
]
56+
content = (HEADER + "\n".join(lines) + "\n") if lines else HEADER
57+
output_path.write_text(content, newline="\n")
58+
59+
def _read_csv(path: Path) -> list[tuple[str, str, str]]:
60+
"""Read the decoded CSV and return valid (type, declaring_type, member) triples."""
61+
with path.open(newline="") as f:
62+
rows = []
63+
for row in csv.reader(f):
64+
if len(row) != 3:
65+
print(f"Skipping malformed row: {row}", file=sys.stderr)
66+
continue
67+
rows.append(tuple(row))
68+
return rows
69+
70+
71+
def main() -> None:
72+
parser = argparse.ArgumentParser(
73+
description="Generate a typemodel.yml from a CodeQL BQRS file."
74+
)
75+
parser.add_argument("codeql", help="Path to the CodeQL checkout")
76+
parser.add_argument("output", help="Output path")
77+
parser.add_argument("db", help="Path to the CodeQL database")
78+
args = parser.parse_args()
79+
80+
generate_type_models(args.output, args.codeql, args.db)
81+
82+
83+
if __name__ == "__main__":
84+
main()

powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class AttributeBaseCfgNode extends AstCfgNode {
136136

137137
private class AttributeChildMapping extends AttributeBaseChildMapping, Attribute {
138138
override predicate relevantChild(Ast child) {
139-
this.relevantChild(child)
139+
super.relevantChild(child)
140140
or
141141
child = this.getANamedArgument()
142142
or
@@ -576,7 +576,10 @@ module ExprNodes {
576576
}
577577

578578
private class ObjectCreationChildMapping extends CallExprChildMapping instanceof ObjectCreation {
579-
override predicate relevantChild(Ast child) { child = super.getConstructedTypeExpr() }
579+
override predicate relevantChild(Ast child) {
580+
super.relevantChild(child) or
581+
child = super.getConstructedTypeExpr()
582+
}
580583
}
581584

582585
class ObjectCreationCfgNode extends CallExprCfgNode {

0 commit comments

Comments
 (0)