1+ /**
2+ * Dependencies module for Infrastructure as Code languages.
3+ */
14private import codeql.Locations
25private import codeql.hcl.Terraform
36
47/**
58 * Dependency for all Infrastructure as Code languages.
69 */
7- class Dependency extends Location {
10+ abstract class Dependency extends Location {
11+ /**
12+ * Gets the name of the dependency.
13+ */
14+ abstract string getName ( ) ;
15+ /**
16+ * Gets the version of the dependency (in a format that is human-readable).
17+ */
18+ abstract string getVersion ( ) ;
19+ /**
20+ * Gets the raw version of the dependency (in a format that is machine-readable).
21+ */
22+ abstract string getRawVersion ( ) ;
23+ /**
24+ * Gets the semantic version of the dependency.
25+ */
26+ abstract SemanticVersion getSemanticVersion ( ) ;
27+ }
28+
29+
30+ /**
31+ * Dependency for Terraform.
32+ */
33+ class TerraformDependency extends Dependency {
834 string name ;
935 string version ;
1036
11- Dependency ( ) {
37+ TerraformDependency ( ) {
1238 // Terraform Provider
1339 exists ( Terraform:: Terraform tf , Terraform:: RequiredProvider rp | rp = tf .getRequiredProvider ( ) |
1440 this = rp .getLocation ( ) and
@@ -19,21 +45,87 @@ class Dependency extends Location {
1945
2046 override string toString ( ) { result = this .getName ( ) + "@" + this .getVersion ( ) }
2147
48+ override string getName ( ) { result = name }
49+
50+
51+ override string getVersion ( ) { result = this .getSemanticVersion ( ) .getPretty ( ) }
52+
53+
54+ override string getRawVersion ( ) { result = version }
55+
56+ override SemanticVersion getSemanticVersion ( ) { result = version }
57+ }
58+
59+ class SemanticVersion extends string {
60+ Dependency dep ;
61+ string normalized ;
62+ string pretty ;
63+
64+ SemanticVersion ( ) {
65+ this = dep .getRawVersion ( ) and
66+ normalized = normalizeSemver ( this ) and
67+ pretty = prettySemver ( this )
68+ }
69+
2270 /**
23- * Gets the name of the dependency .
71+ * Holds if this version may be before `last` .
2472 */
25- string getName ( ) { result = name }
73+ bindingset [ last]
74+ predicate maybeBefore ( string last ) { normalized < normalizeSemver ( last ) }
2675
2776 /**
28- * Gets the version of the dependency .
77+ * Holds if this version may be after `first` .
2978 */
30- string getVersion ( ) { result = version .replaceAll ( "=" , "" ) }
79+ bindingset [ first]
80+ predicate maybeAfter ( string first ) { normalizeSemver ( first ) < normalized }
3181
32- SemanticVersion getSemanticVersion ( ) { result = this .getVersion ( ) }
82+ /**
83+ * Holds if this version may be between `first` (inclusive) and `last` (exclusive).
84+ */
85+ bindingset [ first, last]
86+ predicate maybeBetween ( string first , string last ) {
87+ normalizeSemver ( first ) <= normalized and
88+ normalized < normalizeSemver ( last )
89+ }
90+
91+ /**
92+ * Holds if this version is equivalent to `other`.
93+ */
94+ bindingset [ other]
95+ predicate is ( string other ) { normalized = normalizeSemver ( other ) }
96+
97+ string getPretty ( ) { result = pretty }
3398}
3499
35- class SemanticVersion extends string {
36- private Dependency dep ;
100+ bindingset [ str ]
101+ private string leftPad ( string str ) { result = ( "000" + str ) . suffix ( str . length ( ) ) }
37102
38- SemanticVersion ( ) { this = dep .getVersion ( ) }
103+ /**
104+ * Normalizes a SemVer string such that the lexicographical ordering
105+ * of two normalized strings is consistent with the SemVer ordering.
106+ *
107+ * Pre-release information and build metadata is not yet supported.
108+ */
109+ bindingset [ orig]
110+ private string normalizeSemver ( string orig ) {
111+ exists ( string pattern , string major , string minor , string patch |
112+ pattern = "(=|~>|^)(\\d+)\\.(\\d+)\\.(\\d+)" and
113+ major = orig .regexpCapture ( pattern , 2 ) and
114+ minor = orig .regexpCapture ( pattern , 3 ) and
115+ patch = orig .regexpCapture ( pattern , 4 )
116+ |
117+ result = leftPad ( major ) + "." + leftPad ( minor ) + "." + leftPad ( patch )
118+ )
39119}
120+
121+ bindingset [ orig]
122+ private string prettySemver ( string orig ) {
123+ exists ( string pattern , string major , string minor , string patch |
124+ pattern = "(=|~>|^)(\\d+)\\.(\\d+)\\.(\\d+)" and
125+ major = orig .regexpCapture ( pattern , 2 ) and
126+ minor = orig .regexpCapture ( pattern , 3 ) and
127+ patch = orig .regexpCapture ( pattern , 4 )
128+ |
129+ result = major + "." + minor + "." + patch
130+ )
131+ }
0 commit comments