Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 7 additions & 27 deletions go/ql/lib/semmle/go/dependencies/SemVer.qll
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ overlay[local?]
module;

import semmle.go.dependencies.Dependencies
private import codeql.util.SemVer

/**
* A SemVer-formatted version string in a dependency.
Expand All @@ -17,63 +18,42 @@ class DependencySemVer extends string {

DependencySemVer() {
this = dep.getDepVersion() and
normalized = normalizeSemver(this)
normalized = padSemVer(this)
}

/**
* Holds if this version may be before `last`.
*/
bindingset[last]
predicate maybeBefore(string last) { normalized < normalizeSemver(last) }
predicate maybeBefore(string last) { normalized < padSemVer(last) }

/**
* Holds if this version may be after `first`.
*/
bindingset[first]
predicate maybeAfter(string first) { normalizeSemver(first) < normalized }
predicate maybeAfter(string first) { padSemVer(first) < normalized }

/**
* Holds if this version may be between `first` (inclusive) and `last` (exclusive).
*/
bindingset[first, last]
predicate maybeBetween(string first, string last) {
normalizeSemver(first) <= normalized and
normalized < normalizeSemver(last)
padSemVer(first) <= normalized and
normalized < padSemVer(last)
}

/**
* Holds if this version is equivalent to `other`.
*/
bindingset[other]
predicate is(string other) { normalized = normalizeSemver(other) }
predicate is(string other) { normalized = padSemVer(other) }

/**
* Gets the dependency that uses this string.
*/
Dependency getDependency() { result = dep }
}

bindingset[str]
private string leftPad(string str) { result = ("000" + str).suffix(str.length()) }

/**
* Normalizes a SemVer string such that the lexicographical ordering
* of two normalized strings is consistent with the SemVer ordering.
*
* Pre-release information and build metadata is not yet supported.
*/
bindingset[orig]
private string normalizeSemver(string orig) {
exists(string pattern, string major, string minor, string patch |
pattern = "v?(\\d+)\\.(\\d+)\\.(\\d+)(\\D.*)?" and
major = orig.regexpCapture(pattern, 1) and
minor = orig.regexpCapture(pattern, 2) and
patch = orig.regexpCapture(pattern, 3)
|
result = leftPad(major) + "." + leftPad(minor) + "." + leftPad(patch)
)
}

/**
* A version string in a dependency that has a SemVer, but also contains a git commit SHA.
*
Expand Down
34 changes: 7 additions & 27 deletions javascript/ql/lib/semmle/javascript/dependencies/SemVer.qll
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import semmle.javascript.dependencies.Dependencies
private import codeql.util.SemVer

/**
* A SemVer-formatted version string in a dependency.
Expand All @@ -15,59 +16,38 @@ class DependencySemVer extends string {

DependencySemVer() {
dep.info(_, this) and
normalized = normalizeSemver(this)
normalized = padSemVer(this)
}

/**
* Holds if this version may be before `last`.
*/
bindingset[last]
predicate maybeBefore(string last) { normalized < normalizeSemver(last) }
predicate maybeBefore(string last) { normalized < padSemVer(last) }

/**
* Holds if this version may be after `first`.
*/
bindingset[first]
predicate maybeAfter(string first) { normalizeSemver(first) < normalized }
predicate maybeAfter(string first) { padSemVer(first) < normalized }

/**
* Holds if this version may be between `first` (inclusive) and `last` (exclusive).
*/
bindingset[first, last]
predicate maybeBetween(string first, string last) {
normalizeSemver(first) <= normalized and
normalized < normalizeSemver(last)
padSemVer(first) <= normalized and
normalized < padSemVer(last)
}

/**
* Holds if this version is equivalent to `other`.
*/
bindingset[other]
predicate is(string other) { normalized = normalizeSemver(other) }
predicate is(string other) { normalized = padSemVer(other) }

/**
* Gets the dependency that uses this string.
*/
Dependency getDependency() { result = dep }
}

bindingset[str]
private string leftPad(string str) { result = ("000" + str).suffix(str.length()) }

/**
* Normalizes a SemVer string such that the lexicographical ordering
* of two normalized strings is consistent with the SemVer ordering.
*
* Pre-release information and build metadata is not yet supported.
*/
bindingset[orig]
private string normalizeSemver(string orig) {
exists(string pattern, string major, string minor, string patch |
pattern = "(\\d+)\\.(\\d+)\\.(\\d+)" and
major = orig.regexpCapture(pattern, 1) and
minor = orig.regexpCapture(pattern, 2) and
patch = orig.regexpCapture(pattern, 3)
|
result = leftPad(major) + "." + leftPad(minor) + "." + leftPad(patch)
)
}
50 changes: 6 additions & 44 deletions ruby/ql/lib/codeql/ruby/frameworks/Gemfile.qll
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

private import codeql.ruby.AST
private import codeql.util.SemVer

/**
* Provides classes and predicates for Gemfiles, including version constraint logic.
Expand Down Expand Up @@ -138,7 +139,7 @@ module Gemfile {
exists(int thisMajor, int thisMinor, int otherMajor, int otherMinor |
thisMajor = this.getVersion().getMajor() and
thisMinor = this.getVersion().getMinor() and
exists(string maj, string mi | normalizeSemver(other, _, maj, mi, _) |
exists(string maj, string mi | exists(padSemVer(other, maj, mi, _)) |
otherMajor = maj.toInt() and otherMinor = mi.toInt()
)
|
Expand Down Expand Up @@ -171,26 +172,26 @@ module Gemfile {

Version() {
this = any(Gem c).getAVersionConstraint().getVersionString() and
normalized = normalizeSemver(this)
normalized = padSemVer(this)
}

/**
* Holds if this version is strictly before the version defined by `other`.
*/
bindingset[other]
predicate before(string other) { normalized < normalizeSemver(other) }
predicate before(string other) { normalized < padSemVer(other) }

/**
* Holds if this versino is equal to the version defined by `other`.
*/
bindingset[other]
predicate equal(string other) { normalized = normalizeSemver(other) }
predicate equal(string other) { normalized = padSemVer(other) }

/**
* Holds if this version is strictly after the version defined by `other`.
*/
bindingset[other]
predicate after(string other) { normalized > normalizeSemver(other) }
predicate after(string other) { normalized > padSemVer(other) }

/**
* Holds if this version defines a patch number.
Expand All @@ -212,43 +213,4 @@ module Gemfile {
*/
int getPatch() { result = getPatch(normalized).toInt() }
}

/**
* Normalizes a SemVer string such that the lexicographical ordering
* of two normalized strings is consistent with the SemVer ordering.
*
* Pre-release information and build metadata is not supported.
*/
bindingset[orig]
private predicate normalizeSemver(
string orig, string normalized, string major, string minor, string patch
) {
major = getMajor(orig) and
(
minor = getMinor(orig)
or
not exists(getMinor(orig)) and minor = "0"
) and
(
patch = getPatch(orig)
or
not exists(getPatch(orig)) and patch = "0"
) and
normalized = leftPad(major) + "." + leftPad(minor) + "." + leftPad(patch)
}

bindingset[orig]
private string normalizeSemver(string orig) { normalizeSemver(orig, result, _, _, _) }

bindingset[s]
private string getMajor(string s) { result = s.regexpCapture("(\\d+).*", 1) }

bindingset[s]
private string getMinor(string s) { result = s.regexpCapture("(\\d+)\\.(\\d+).*", 2) }

bindingset[s]
private string getPatch(string s) { result = s.regexpCapture("(\\d+)\\.(\\d+)\\.(\\d+).*", 3) }

bindingset[str]
private string leftPad(string str) { result = ("000" + str).suffix(str.length()) }
}
26 changes: 21 additions & 5 deletions rust/ql/lib/codeql/rust/internal/PathResolution.qll
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ private import codeql.rust.elements.internal.CallExprImpl::Impl as CallExprImpl
private import codeql.rust.internal.CachedStages
private import codeql.rust.frameworks.stdlib.Builtins as Builtins
private import codeql.util.Option
private import codeql.util.SemVer

private newtype TNamespace =
TTypeNamespace() or
Expand Down Expand Up @@ -568,6 +569,16 @@ class CrateItemNode extends NamedItemNode instanceof Crate {
)
}

pragma[nomagic]
predicate isLatestVersion(string name) {
this =
max(CrateItemNode c, string ver |
name = c.getName() and ver = padSemVer(c.(Crate).getVersion())
|
c order by ver
Comment thread
hvitved marked this conversation as resolved.
)
}

override string getName() { result = Crate.super.getName() }

override Namespace getNamespace() {
Expand Down Expand Up @@ -1529,11 +1540,11 @@ private predicate crateDependencyEdge(SourceFileItemNode file, string name, Crat
crateDependency(file, name, dep)
or
// As a fallback, give all files access to crates that do not conflict with known dependencies
// and declarations. This is in order to workaround incomplete crate dependency information
// provided by the extractor, as well as `CrateItemNode.getASourceFile()` being unable to map
// a given file to its crate (for example, if the file is `mod` imported inside a macro that the
// extractor is unable to expand).
name = dep.getName() and
// and declarations, as long as those crates have a unique latest version.
// This is in order to workaround incomplete crate dependency information provided by the extractor,
// as well as `CrateItemNode.getASourceFile()` being unable to map a given file to its crate (for
// example, if the file is `mod` imported inside a macro that the extractor is unable to expand).
dep = unique(CrateItemNode dep0 | dep0.isLatestVersion(name)) and
Comment thread
hvitved marked this conversation as resolved.
not hasDeclOrDep(file, name)
}

Expand Down Expand Up @@ -2385,6 +2396,11 @@ private module Debug {
useImportEdge(use, name, item, kind)
}

predicate debugCrateDependencyEdge(SourceFileItemNode file, string name, CrateItemNode dep) {
file = getRelevantLocatable() and
crateDependencyEdge(file, name, dep)
}

ItemNode debugGetASuccessor(ItemNode i, string name, SuccessorKind kind) {
i = getRelevantLocatable() and
result = i.getASuccessor(name, kind, _)
Expand Down
57 changes: 57 additions & 0 deletions shared/util/codeql/util/SemVer.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Provides logic for working SemVer (Semantic Versioning).
*/
overlay[local?]
module;

bindingset[str]
private string leftPad(string str) { result = ("0000" + str).suffix(str.length()) }
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
hvitved marked this conversation as resolved.
Dismissed

/**
* Gets the major number of a SemVer string.
*/
bindingset[s]
string getMajor(string s) { result = s.regexpCapture("v?(\\d+).*", 1) }

/**
* Gets the minor number of a SemVer string.
*/
bindingset[s]
string getMinor(string s) { result = s.regexpCapture("v?(\\d+)\\.(\\d+).*", 2) }

/**
* Gets the patch number of a SemVer string.
*/
bindingset[s]
string getPatch(string s) { result = s.regexpCapture("v?(\\d+)\\.(\\d+)\\.(\\d+).*", 3) }

/**
* Normalizes a SemVer string such that the lexicographical ordering
* of two normalized strings is consistent with the SemVer ordering.
*
* Pre-release information and build metadata is not yet supported.
*/
bindingset[orig]
string padSemVer(string orig, string major, string minor, string patch) {
major = getMajor(orig) and
(
minor = getMinor(orig)
or
not exists(getMinor(orig)) and minor = "0"
) and
(
patch = getPatch(orig)
or
not exists(getPatch(orig)) and patch = "0"
) and
result = leftPad(major) + "." + leftPad(minor) + "." + leftPad(patch)
}

/**
* Normalizes a SemVer string such that the lexicographical ordering
* of two normalized strings is consistent with the SemVer ordering.
*
* Pre-release information and build metadata is not yet supported.
*/
bindingset[orig]
string padSemVer(string orig) { result = padSemVer(orig, _, _, _) }
Loading