Skip to content

Commit f920bc1

Browse files
committed
C#: Update model generator to output one file per namespace
1 parent 7aed6f9 commit f920bc1

File tree

3 files changed

+51
-67
lines changed

3 files changed

+51
-67
lines changed

misc/scripts/models-as-data/convert_extensions.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,20 @@
77
import sys
88
import tempfile
99

10-
1110
def quote_if_needed(v):
1211
# string columns
1312
if type(v) is str:
1413
return "\"" + v + "\""
1514
# bool column
1615
return str(v)
1716

18-
def insert_update(rows, key, value):
19-
if key in rows:
20-
rows[key] += value
21-
else:
22-
rows[key] = value
23-
24-
def merge(*dicts):
25-
merged = {}
26-
for d in dicts:
27-
for entry in d:
28-
insert_update(merged, entry, d[entry])
29-
return merged
30-
3117
def parseData(data):
3218
rows = [{ }, { }]
3319
for row in data:
3420
d = map(quote_if_needed, row)
3521
provenance = row[-1]
3622
targetRows = rows[1] if provenance.endswith("generated") else rows[0]
37-
insert_update(targetRows, row[0], " - [" + ', '.join(d) + ']\n')
23+
helpers.insert_update(targetRows, row[0], " - [" + ', '.join(d) + ']\n')
3824

3925
return rows
4026

@@ -78,7 +64,7 @@ def makeContent(self):
7864
sources = self.getAddsTo("ExtractSources.ql", helpers.sourceModelPredicate)
7965
sinks = self.getAddsTo("ExtractSinks.ql", helpers.sinkModelPredicate)
8066
neutrals = self.getAddsTo("ExtractNeutrals.ql", helpers.neutralModelPredicate)
81-
return [merge(sources[0], sinks[0], summaries[0], neutrals[0]), merge(sources[1], sinks[1], summaries[1], neutrals[1])]
67+
return [helpers.merge(sources[0], sinks[0], summaries[0], neutrals[0]), helpers.merge(sources[1], sinks[1], summaries[1], neutrals[1])]
8268

8369

8470
def save(self, extensions):

misc/scripts/models-as-data/generate_flow_model.py

Lines changed: 36 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ def quote_if_needed(row):
1313
if row != "true" and row != "false":
1414
return "\"" + row + "\""
1515
# subtypes column
16-
return row
16+
return row[0].upper() + row[1:]
1717

1818
def parseData(data):
19-
rows = ""
20-
for (row) in data:
19+
rows = { }
20+
21+
for row in data:
2122
d = row[0].split(';')
23+
namespace = d[0]
2224
d = map(quote_if_needed, d)
23-
rows += " - [" + ', '.join(d) + ']\n'
25+
helpers.insert_update(rows, namespace, " - [" + ', '.join(d) + ']\n')
2426

2527
return rows
2628

@@ -38,12 +40,10 @@ def __init__ (self, language):
3840

3941
def printHelp(self):
4042
print(f"""Usage:
41-
python3 GenerateFlowModel.py <library-database> <outputYml> [<friendlyFrameworkName>] [--with-sinks] [--with-sources] [--with-summaries] [--with-typebased-summaries] [--dry-run]
43+
python3 GenerateFlowModel.py <library-database> [--with-sinks] [--with-sources] [--with-summaries] [--with-typebased-summaries] [--dry-run]
4244
4345
This generates summary, source and sink models for the code in the database.
44-
The files will be placed in `{self.language}/ql/lib/ext/generated/<outputYml>.model.yml` where
45-
outputYml is the name (and path) of the output YAML file. Usually, models are grouped by their
46-
respective frameworks.
46+
The files will be placed in `{self.language}/ql/lib/ext/generated/`.
4747
4848
Which models are generated is controlled by the flags:
4949
--with-sinks
@@ -57,28 +57,18 @@ def printHelp(self):
5757
5858
Example invocations:
5959
$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db mylibrary
60-
$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db mylibrary "Friendly Name of Framework"
60+
$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db mylibrary
6161
$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db --with-sinks
6262
6363
Requirements: `codeql` should both appear on your path.
6464
""")
6565

6666

67-
def setenvironment(self, target, database, friendlyName):
67+
def setenvironment(self, database):
6868
self.codeQlRoot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
69-
if not target.endswith(".model.yml"):
70-
target += ".model.yml"
71-
filename = os.path.basename(target)
72-
if friendlyName is not None:
73-
self.friendlyname = friendlyName
74-
else:
75-
self.friendlyname = filename[:-10]
76-
self.shortname = filename[:-10]
7769
self.database = database
7870
self.generatedFrameworks = os.path.join(
7971
self.codeQlRoot, f"{self.language}/ql/lib/ext/generated/")
80-
self.frameworkTarget = os.path.join(self.generatedFrameworks, filename)
81-
self.typeBasedFrameworkTarget = os.path.join(self.generatedFrameworks, "TypeBased" + filename)
8272
self.workDir = tempfile.mkdtemp()
8373
os.makedirs(self.generatedFrameworks, exist_ok=True)
8474

@@ -117,15 +107,11 @@ def make(language):
117107
if not generator.generateSinks and not generator.generateSources and not generator.generateSummaries and not generator.generateNeutrals and not generator.generateTypeBasedSummaries:
118108
generator.generateSinks = generator.generateSources = generator.generateSummaries = generator.generateNeutrals = True
119109

120-
if len(sys.argv) < 3 or len(sys.argv) > 4:
110+
if len(sys.argv) < 2:
121111
generator.printHelp()
122112
sys.exit(1)
123113

124-
friendlyName = None
125-
if len(sys.argv) == 4:
126-
friendlyName = sys.argv[3]
127-
128-
generator.setenvironment(sys.argv[2], sys.argv[1], friendlyName)
114+
generator.setenvironment(sys.argv[1])
129115
return generator
130116

131117

@@ -141,58 +127,57 @@ def runQuery(self, query):
141127

142128

143129
def asAddsTo(self, rows, predicate):
144-
if rows.strip() == "":
145-
return ""
146-
return helpers.addsToTemplate.format(f"codeql/{self.language}-all", predicate, rows)
147-
130+
extensions = { }
131+
for key in rows:
132+
extensions[key] = helpers.addsToTemplate.format(f"codeql/{self.language}-all", predicate, rows[key])
133+
return extensions
148134

149135
def getAddsTo(self, query, predicate):
150136
data = self.runQuery(query)
151137
rows = parseData(data)
152138
return self.asAddsTo(rows, predicate)
153139

154-
155140
def makeContent(self):
156141
if self.generateSummaries:
157142
summaryAddsTo = self.getAddsTo("CaptureSummaryModels.ql", helpers.summaryModelPredicate)
158143
else:
159-
summaryAddsTo = ""
144+
summaryAddsTo = { }
160145

161146
if self.generateSinks:
162147
sinkAddsTo = self.getAddsTo("CaptureSinkModels.ql", helpers.sinkModelPredicate)
163148
else:
164-
sinkAddsTo = ""
149+
sinkAddsTo = { }
165150

166151
if self.generateSources:
167152
sourceAddsTo = self.getAddsTo("CaptureSourceModels.ql", helpers.sourceModelPredicate)
168153
else:
169-
sourceAddsTo = ""
154+
sourceAddsTo = {}
170155

171156
if self.generateNeutrals:
172157
neutralAddsTo = self.getAddsTo("CaptureNeutralModels.ql", helpers.neutralModelPredicate)
173158
else:
174-
neutralAddsTo = ""
159+
neutralAddsTo = { }
175160

176-
return f"""# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
177-
# Definitions of models for the {self.friendlyname} framework.
178-
extensions:
179-
{sinkAddsTo}{sourceAddsTo}{summaryAddsTo}{neutralAddsTo}"""
161+
return helpers.merge(summaryAddsTo, sinkAddsTo, sourceAddsTo, neutralAddsTo)
180162

181163
def makeTypeBasedContent(self):
182164
if self.generateTypeBasedSummaries:
183-
typeBasedSummaryAddsTo = self.getAddsTo("CaptureTypeBasedSummaryModels.ql", "extSummaryModel")
165+
typeBasedSummaryAddsTo = self.getAddsTo("CaptureTypeBasedSummaryModels.ql", helpers.summaryModelPredicate)
184166
else:
185-
typeBasedSummaryAddsTo = ""
167+
typeBasedSummaryAddsTo = { }
186168

187-
return f"""# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
188-
# Definitions of type based summaries in the {self.friendlyname} framework.
189-
extensions:
190-
{typeBasedSummaryAddsTo}"""
169+
return typeBasedSummaryAddsTo
191170

192-
def save(self, content, target):
193-
with open(target, "w") as targetYml:
194-
targetYml.write(content)
195-
print("Models as data extensions written to " + target)
171+
def save(self, extensions, extension):
172+
# Create a file for each namespace and save models.
173+
extensionTemplate = """# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
174+
extensions:
175+
{0}"""
176+
for entry in extensions:
177+
target = os.path.join(self.generatedFrameworks, entry + extension)
178+
with open(target, "w") as f:
179+
f.write(extensionTemplate.format(extensions[entry]))
180+
print("Models as data extensions written to " + target)
196181

197182

198183
def run(self):
@@ -204,7 +189,7 @@ def run(self):
204189
sys.exit(0)
205190

206191
if self.generateSinks or self.generateSinks or self.generateSummaries:
207-
self.save(content, self.frameworkTarget)
192+
self.save(content, ".model.yml")
208193

209194
if self.generateTypeBasedSummaries:
210-
self.save(typeBasedContent, self.typeBasedFrameworkTarget)
195+
self.save(typeBasedContent, ".typebased.model.yml")

misc/scripts/models-as-data/helpers.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,16 @@ def readData(workDir, bqrsFile):
3838
except KeyError:
3939
print('Unexpected JSON output - no tuples found')
4040
exit(1)
41+
42+
def insert_update(rows, key, value):
43+
if key in rows:
44+
rows[key] += value
45+
else:
46+
rows[key] = value
47+
48+
def merge(*dicts):
49+
merged = {}
50+
for d in dicts:
51+
for entry in d:
52+
insert_update(merged, entry, d[entry])
53+
return merged

0 commit comments

Comments
 (0)