Skip to content

Commit 9af18bc

Browse files
committed
WIP: add dgml/dot output/remove test code
1 parent e027b0e commit 9af18bc

File tree

7 files changed

+162
-179
lines changed

7 files changed

+162
-179
lines changed

cpp/ql/lib/experimental/Quantum/Base.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
5151
* This predicate is used by derived classes to construct the graph of cryptographic operations.
5252
*/
5353
predicate properties(string key, string value, Location location) {
54-
key = "origin" and location = this.getOrigin(value).getLocation()
54+
key = "origin" and
55+
location = this.getOrigin(value).getLocation() and
56+
not location = this.getLocation()
5557
}
5658

5759
/**

cpp/ql/lib/experimental/Quantum/BaseBackup.qll

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

cpp/ql/lib/experimental/Quantum/OpenSSL.qll

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,6 @@ module OpenSSLModel {
8484
}
8585
}
8686

87-
class TestKeyDerivationOperationHacky extends KeyDerivationOperation instanceof FunctionCall {
88-
HKDF hkdf;
89-
90-
TestKeyDerivationOperationHacky() {
91-
this.getEnclosingFunction() = hkdf.(Expr).getEnclosingFunction()
92-
}
93-
94-
override Crypto::KeyDerivationAlgorithm getAlgorithm() { result = hkdf }
95-
}
96-
9787
class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF instanceof Expr {
9888
KDFAlgorithmStringLiteral origin;
9989

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @name "Outputs a graph representation of the cryptographic bill of materials."
3+
* @kind graph
4+
* @id cbomgraph
5+
*/
6+
7+
import experimental.Quantum.Language
8+
9+
string getPropertyString(Crypto::NodeBase node, string key) {
10+
result =
11+
strictconcat(any(string value, Location location, string parsed |
12+
node.properties(key, value, location) and
13+
parsed = "(" + value + "," + location.toString() + ")"
14+
|
15+
parsed
16+
), ","
17+
)
18+
}
19+
20+
string getLabel(Crypto::NodeBase node) { result = node.toString() }
21+
22+
query predicate nodes(Crypto::NodeBase node, string key, string value) {
23+
key = "semmle.label" and
24+
value = getLabel(node)
25+
or
26+
// CodeQL's DGML output does not include a location
27+
key = "Location" and
28+
value = node.getLocation().toString()
29+
or
30+
// Known unknown edges should be reported as properties rather than edges
31+
node = node.getChild(key) and
32+
value = "<unknown>"
33+
or
34+
// Report properties
35+
value = getPropertyString(node, key)
36+
}
37+
38+
query predicate edges(Crypto::NodeBase source, Crypto::NodeBase target, string key, string value) {
39+
key = "semmle.label" and
40+
target = source.getChild(value) and
41+
// Known unknowns are reported as properties rather than edges
42+
not source = target
43+
}
44+
45+
query predicate graphProperties(string key, string value) {
46+
key = "semmle.graphKind" and value = "graph"
47+
}

cpp/ql/src/experimental/Quantum/Test.ql

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/bash
2+
3+
CODEQL_PATH="/Users/nicolaswill/Library/Application Support/Code/User/globalStorage/github.vscode-codeql/distribution5/codeql/codeql"
4+
DATABASE_PATH="/Users/nicolaswill/openssl_codeql/openssl/openssl_db"
5+
QUERY_FILE="CBOMGraph.ql"
6+
OUTPUT_DIR="graph_output"
7+
8+
python3 generate_cbom.py -c "$CODEQL_PATH" -d "$DATABASE_PATH" -q "$QUERY_FILE" -o "$OUTPUT_DIR"
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import sys
5+
import argparse
6+
import subprocess
7+
import xml.etree.ElementTree as ET
8+
9+
def run_codeql_analysis(codeql_path, database_path, query_path, output_dir):
10+
"""Runs the CodeQL analysis and generates a DGML file."""
11+
os.makedirs(output_dir, exist_ok=True)
12+
command = [
13+
codeql_path, "database", "analyze", database_path, query_path,
14+
"--rerun", "--format=dgml", "--output", output_dir
15+
]
16+
17+
print(f"Running CodeQL analysis: {' '.join(command)}")
18+
result = subprocess.run(command, capture_output=True, text=True)
19+
20+
if result.returncode == 0:
21+
print("Analysis completed successfully.")
22+
else:
23+
print("Analysis failed.")
24+
print(result.stderr)
25+
sys.exit(1)
26+
27+
return result.returncode
28+
29+
30+
def convert_dgml_to_dot(dgml_file, dot_file):
31+
"""Converts the DGML file to DOT format using the exact original implementation."""
32+
print(f"Processing DGML file: {dgml_file}")
33+
34+
# Read source DGML
35+
with open(dgml_file, "r", encoding="utf-8") as f:
36+
xml_content = f.read()
37+
38+
root = ET.fromstring(xml_content)
39+
40+
# Form dot element sequence
41+
body_l = ["digraph cbom {",
42+
"node [shape=box];",
43+
"rankdir=LR;"
44+
]
45+
46+
# Process nodes
47+
for node in root.find("{http://schemas.microsoft.com/vs/2009/dgml}Nodes"):
48+
att = node.attrib
49+
node_id = att['Id']
50+
label_parts = []
51+
for key, value in att.items():
52+
if key == 'Id':
53+
continue
54+
elif key == 'Label':
55+
label_parts.append(value)
56+
else:
57+
label_parts.append(f"{key}={value}")
58+
label = "\\n".join(label_parts)
59+
prop_l = [f'label="{label}"']
60+
node_s = f'nd_{node_id} [{", ".join(prop_l)}];'
61+
body_l.append(node_s)
62+
63+
# Process edges
64+
for edge in root.find("{http://schemas.microsoft.com/vs/2009/dgml}Links"):
65+
att = edge.attrib
66+
edge_s = 'nd_{} -> nd_{} [label="{}"];'.format(
67+
att["Source"], att["Target"], att.get("Label", ""))
68+
body_l.append(edge_s)
69+
70+
body_l.append("}")
71+
72+
# Write DOT output
73+
with open(dot_file, "w", encoding="utf-8") as f:
74+
f.write("\n".join(body_l))
75+
76+
print(f"DGML file successfully converted to DOT format: {dot_file}")
77+
78+
79+
def main():
80+
parser = argparse.ArgumentParser(description="Run CodeQL analysis and convert DGML to DOT.")
81+
parser.add_argument("-c", "--codeql", required=True, help="Path to CodeQL CLI executable.")
82+
parser.add_argument("-d", "--database", required=True, help="Path to the CodeQL database.")
83+
parser.add_argument("-q", "--query", required=True, help="Path to the .ql query file.")
84+
parser.add_argument("-o", "--output", required=True, help="Output directory for analysis results.")
85+
86+
args = parser.parse_args()
87+
88+
# Run CodeQL analysis
89+
run_codeql_analysis(args.codeql, args.database, args.query, args.output)
90+
91+
# Locate DGML file
92+
dgml_file = os.path.join(args.output, "cbomgraph.dgml")
93+
dot_file = dgml_file.replace(".dgml", ".dot")
94+
95+
if os.path.exists(dgml_file):
96+
# Convert DGML to DOT
97+
convert_dgml_to_dot(dgml_file, dot_file)
98+
else:
99+
print(f"No DGML file found in {args.output}.")
100+
sys.exit(1)
101+
102+
103+
if __name__ == "__main__":
104+
main()

0 commit comments

Comments
 (0)