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
49 changes: 41 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions contrib/codeql/lib/imports.qll
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,18 @@ predicate isMacroReexport(Use u) {
)
}

/**
* Holds if `u` is an allowlisted re-export of a marker subcrate
* through its owning crate (e.g. `dash-types-marker` via `dash-types`).
*/
/** Holds if `u` is an allowlisted re-export from a foreign crate. */
private predicate isAllowlistedReexport(Use u) {
usePrefix(u) = "dash_types_marker" and
fileOf(u).getAbsolutePath().matches("%pkgs/types/%")
or
usePrefix(u) = "dash_pkc" and
u.getUseTree().getPath().getSegment().getIdentifier().getText() = "__PubKeyHash" and
fileOf(u).getAbsolutePath().matches("%pkgs/script/%")
or
usePrefix(u) = "dash_types" and
u.getUseTree().getPath().getSegment().getIdentifier().getText() = "__ScriptHash" and
fileOf(u).getAbsolutePath().matches("%pkgs/script/%")
}

/**
Expand Down
4 changes: 3 additions & 1 deletion contrib/codeql/lib/policy.qll
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ predicate isSecretType(TypeItem t) {
// A share *of a signature* is published, so it holds nothing to protect. Excluded by
// exact name because `SecretKeyShare` and `RawShare` match the same Share substring
// and do carry secret scalars.
not t.getName().getText() = "SignatureShare"
not t.getName().getText() = "SignatureShare" and
// Serde artifact to deserialize a tagged enum.
not t.getName().getText() = "__Seed"
}

/**
Expand Down
59 changes: 47 additions & 12 deletions contrib/codeql/zeroize.ql
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,49 @@ predicate callsCtEq(Function f) {
}

/**
* Holds if `f` decides something by a comparison that stops early, described
* by `how`.
* Holds if `e` reads byte storage rather than an opaque value.
*
* The short-circuiting adapters walk only as far as the first byte that settles
* the answer, and `==` on a byte container lowers to `memcmp`, which does the
* same.
* Fields are judged by their declared type, resolved through the type layer, so
* a flag sitting beside the bytes is not mistaken for them. References and
* derefs are looked through.
*/
predicate bytesExpr(Expr e) {
e instanceof ArrayExpr
or
e.(MethodCallExpr).getIdentifier().getText() =
["as_bytes", "as_ref", "as_slice", "to_bytes", "into_bytes", "as_array", "expose_secret"]
or
fieldMayHoldSecret(e.(FieldExpr).getStructField().getTypeRepr())
or
fieldMayHoldSecret(e.(FieldExpr).getTupleField().getTypeRepr())
or
bytesExpr(e.(RefExpr).getExpr())
or
bytesExpr(e.(PrefixExpr).getExpr())
}

/**
* Holds if `f` compares byte storage with `how`, an operator that stops early.
*
* `==` on bytes compiles to `memcmp`, which short-circuits on the first
* mismatch. The operand gate keeps the rule on byte storage, so deciding on
* a flag, a length, or an enum discriminant beside the secret is not
* reported. Secrecy itself is not judged here: `variableTimeSecretTest`
* supplies that through `enforcedSecretType`.
*/
predicate comparesBytes(Function f, string how) {
exists(BinaryExpr be |
be.getEnclosingCallable() = f and
be.getOperatorName() = ["==", "!="] and
bytesExpr([be.getLhs(), be.getRhs()]) and
how = be.getOperatorName()
)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Holds if `f` decides something with a short-circuiting adapter, named `how`.
*
* These walk only as far as the first byte that settles the answer.
*/
predicate stopsEarly(Function f, string how) {
exists(MethodCallExpr mc, string name |
Expand All @@ -227,12 +264,6 @@ predicate stopsEarly(Function f, string how) {
name = ["all", "any", "position", "find", "contains", "starts_with", "ends_with"] and
how = name + "()"
)
or
exists(BinaryExpr be |
be.getEnclosingCallable() = f and
be.getOperatorName() = ["==", "!="] and
how = be.getOperatorName()
)
}

/**
Expand All @@ -251,7 +282,11 @@ predicate variableTimeSecretTest(Function f, string how) {
not isTestCode(f) and
not f.getName().getText() = "eq" and
typeHead(f.getRetType().getTypeRepr()) = "bool" and
stopsEarly(f, how) and
(
stopsEarly(f, how)
or
comparesBytes(f, how)
) and
not callsCtEq(f)
)
}
Expand Down
2 changes: 1 addition & 1 deletion contrib/lint/lint_codeql.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
)

_SOURCE_KEYWORDS = (
"Serialize", "Deserialize", "Unencodable", "TypeId", "#[cfg",
"Serialize", "Deserialize", "Unencodable", "TypeId", "Zeroize", "#[cfg",
)


Expand Down
Loading
Loading