Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4a0c05f
sdk%chore: update rulers in vscode to match CodeQL, Rust linting
kwvg Aug 3, 2026
00cd249
sdk%lint: enable stricter rules for `script`, fix violations
kwvg Jul 30, 2026
fc31bd0
sdk%lint: enable stricter rules for `pkc`, fix violations
kwvg Jul 30, 2026
193e7a2
sdk%fix: add redacted `Debug` impls for `Fr` and `VecEncoder`
kwvg Aug 2, 2026
9cca2af
types%feat(codec): enrich `DecodeError` with variant, error propagation
kwvg Aug 2, 2026
ac24140
types%feat: add enum helper macro, propagate to `primitives`, `script`
kwvg Aug 2, 2026
877f105
pkc%refac: restructure `k256` module into `ecdsa` with operational types
kwvg Aug 2, 2026
610c541
pkc%refac(ecdsa): expose `EcdsaError` without the feature gate
kwvg Jul 31, 2026
daef291
pkc%refac(tests): inline ecdsa discrete tests into source modules
kwvg Jul 31, 2026
9d19611
types%refac(codec): fold the byte-newtype trait set into `derive_bytes!`
kwvg Aug 2, 2026
76b5572
types%feat(codec): add fixed-width `Arr{En,De}coder` for sensitive bufs
kwvg Aug 3, 2026
0a19896
sdk%lint(codeql): resolve trait impls through the type layer
kwvg Aug 2, 2026
4e2c3ca
sdk%lint(codeql): add width, derive, zero and compare rules for secrets
kwvg Aug 2, 2026
600372c
pkc%feat(ecdsa): implement `EcdsaSkBytes` as `zeroize`d secret byte bag
kwvg Jul 31, 2026
ab31069
pkc%feat(bls): define `BlsSchemeId` scheme discriminator trait
kwvg Aug 2, 2026
4fe9fe3
pkc%feat(bls): define supported signature schemes w/ `BlsSigId`
kwvg Aug 3, 2026
b5892ef
pkc%feat(bls): implement `BlsPkBytes` as public key byte bag
kwvg Aug 2, 2026
8dc0273
pkc%feat(bls): implement `BlsSkBytes` as `zeroize`d secret byte bag
kwvg Aug 2, 2026
7ea891c
pkc%feat(bls): implement `BlsSigBytes` as signature byte bag
kwvg Aug 2, 2026
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
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
{
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer",
"editor.rulers": [80, 120],
},
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.rulers": [80]
},
"[ql]": {
"editor.rulers": [100]
},
"editor.formatOnSave": true,
"editor.minimap.enabled": true,
"editor.rulers": [120],
Expand Down
6 changes: 6 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions contrib/codeql/lib/files.qll
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ predicate fileRelPath(File f, string relPath) {
relPath = f.getAbsolutePath().regexpCapture(".*/(pkgs/.*)", 1)
}

/** Holds if `f` belongs to a crate in this workspace. */
predicate isWorkspaceFile(File f) { fileRelPath(f, _) }

/** Holds if module `m` is not nested inside another module. */
predicate isRootModule(Module m) {
not exists(Module enclosing | m.getParentNode() = enclosing.getItemList())
Expand Down
38 changes: 6 additions & 32 deletions contrib/codeql/lib/filters.qll
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import lib.files
import lib.types
import rust

/** Materialises function spans per file for containment checks. */
Expand Down Expand Up @@ -40,9 +41,9 @@ private predicate testModuleSpan(File file, int mStart, int mEnd) {
)
}

/** Holds if `t` is inside a test module or test file. */
predicate isTestCode(TypeItem t) {
fileOf(t).getAbsolutePath().matches("%/tests/%")
/** Holds if `t` is inside a test module, test file, or benchmark. */
predicate isTestCode(Locatable t) {
fileOf(t).getAbsolutePath().matches(["%/tests/%", "%/bench/%"])
or
exists(File file, int mStart, int mEnd |
testModuleSpan(file, mStart, mEnd) and
Expand Down Expand Up @@ -93,32 +94,5 @@ string cratePrefix(TypeItem t) {
)
}

/** Gets the type name of a field in struct `s`. */
string structFieldTypeName(Struct s) {
exists(PathTypeRepr tr |
tr = s.getFieldList().(StructFieldList).getAField().getTypeRepr() or
tr = s.getFieldList().(TupleFieldList).getField(_).getTypeRepr()
|
result = tr.getPath().getSegment().getIdentifier().getText()
)
}

/** Gets the type name of a field in enum variant of `e`. */
string enumFieldTypeName(Enum e) {
exists(Variant v, PathTypeRepr tr |
v = e.getVariantList().getAVariant() and
(
tr = v.getFieldList().(StructFieldList).getAField().getTypeRepr() or
tr = v.getFieldList().(TupleFieldList).getField(_).getTypeRepr()
)
|
result = tr.getPath().getSegment().getIdentifier().getText()
)
}

/** Gets the type name of a field in type item `t` (struct or enum). */
string typeFieldName(TypeItem t) {
result = structFieldTypeName(t)
or
result = enumFieldTypeName(t)
}
/** Gets the type name of a field in type item `t` (struct, enum, or union). */
string typeFieldName(TypeItem t) { result = typeHead(fieldTypeRepr(t)) }
41 changes: 33 additions & 8 deletions contrib/codeql/lib/policy.qll
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import lib.files
import lib.filters
import lib.source_lines
import lib.traits
import lib.types
import rust

/** Holds if `t` carries `#[derive(...name...)]` detected via source-line scanning. */
Expand Down Expand Up @@ -39,13 +40,31 @@ predicate isNotEncodable(TypeItem t) {

/** Holds if `t` holds secret or security-sensitive material. */
predicate isSecretType(TypeItem t) {
t.getName().getText().regexpMatch(".*(Secret|Private|Seed|Password|Mnemonic|Share).*") and
// Exclude types whose name contains "Shared" (e.g. SharedState),
// which match the Share substring but are not secret holders.
not t.getName().getText().regexpMatch(".*Shared.*")
or
// Scalar field wrapper holding secret key material
t.getName().getText() = "Fr"
(
t.getName().getText().regexpMatch(".*(Secret|Private|Seed|Password|Mnemonic|SkBytes).*")
or
// "Share" is the one keyword that "Shared" (e.g. SharedState) matches without holding a secret,
// so the guard applies to it alone, exceptions to this rule are explicitly enumerated.
t.getName().getText().regexpMatch(".*Share.*") and
not t.getName().getText().regexpMatch(".*Shared.*")
or
// Scalar field wrapper holding secret key material
t.getName().getText() = "Fr"
) and
// 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"
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Holds if `tr` names a heap-growable container.
*
* A `Vec` or `String` can reallocate while being filled, stranding a copy at
* the old allocation that drop-time wiping cannot reach.
*/
predicate isGrowableType(TypeRepr tr) {
typeHead(tr) = ["Vec", "String", "VecDeque", "BTreeMap", "BTreeSet", "BinaryHeap"]
}

/** Holds if `t` is an iterator type (name ends with Iterator or Iter). */
Expand Down Expand Up @@ -252,12 +271,18 @@ predicate isEnforcedCrate(File f) {
f.getAbsolutePath().matches("%/pkgs/primitives/%")
or
f.getAbsolutePath().matches("%/pkgs/p2p_core/%")
or
f.getAbsolutePath().matches("%/pkgs/pkc/%")
or
f.getAbsolutePath().matches("%/pkgs/script/%")
}

/** Holds if file `f` is in a crate that can derive `Unencodable`. */
predicate isUnencodableCrate(File f) {
f.getAbsolutePath().matches("%/pkgs/primitives/%") or
f.getAbsolutePath().matches("%/pkgs/p2p_core/%")
f.getAbsolutePath().matches("%/pkgs/p2p_core/%") or
f.getAbsolutePath().matches("%/pkgs/pkc/%") or
f.getAbsolutePath().matches("%/pkgs/script/%")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

/** Declaration slots that define the required source ordering. */
Expand Down
43 changes: 22 additions & 21 deletions contrib/codeql/lib/traits.qll
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,19 @@ private predicate implTraitHasCrate(Impl i, string traitName, string crate) {
)
}

/** Gets the trait name from an impl block's trait reference. */
string implTraitName(Impl i) { result = implTraitPath(i).getSegment().getIdentifier().getText() }
/**
* Gets the trait name from an impl block's trait reference.
*
* Prefers the resolved `Trait` item so an aliased or fully qualified path
* still reports the trait's own name, falling back to the written path when
* the trait lives in a crate the extractor did not resolve.
*/
string implTraitName(Impl i) {
result = i.getTrait().getName().getText()
or
not exists(i.getTrait()) and
result = implTraitPath(i).getSegment().getIdentifier().getText()
}

/** Gets the type name from an impl block's self type. */
string implSelfName(Impl i) {
Expand Down Expand Up @@ -70,13 +81,6 @@ private predicate manualImplInfo(Impl i, File f, string selfName, string traitNa
scope = i.(AstNode).getParentNode()
}

/** Holds if `t` has a manual impl for `traitName`. */
predicate hasManualImpl(TypeItem t, string traitName) {
exists(Impl i |
manualImplInfo(i, fileOf(t), t.getName().getText(), traitName, t.(AstNode).getParentNode())
)
}

/** Materialises macro impl metadata for join efficiency. */
pragma[nomagic]
private predicate macroImplInfo(MacroItems m, Impl i, File f, string selfName, string traitName) {
Expand All @@ -86,19 +90,16 @@ private predicate macroImplInfo(MacroItems m, Impl i, File f, string selfName, s
traitName = implTraitName(i)
}

/** Holds if `t` has a macro-generated (non-derive) impl for `traitName`. */
predicate hasMacroImpl(TypeItem t, string traitName) {
exists(MacroItems m, Impl i |
macroImplInfo(m, i, fileOf(t), t.getName().getText(), traitName) and
not m = t.getADeriveMacroExpansion()
)
}

/** Holds if `t` implements `traitName` via derive, manual impl, or macro. */
/**
* Holds if `t` implements `traitName`.
*
* Resolves the impl's self type rather than matching it by name and enclosing
* scope, so generic impls (`impl<S: Marker> Zeroize for Bag<S>`), impls written
* in another module, and `macro_rules!`-generated impls are all covered without
* enumerating where they may appear.
*/
predicate implementsTrait(TypeItem t, string traitName) {
hasDerivedImpl(t, traitName) or
hasManualImpl(t, traitName) or
hasMacroImpl(t, traitName)
exists(Impl i | i.getSelf() = t and implTraitName(i) = traitName)
}

/**
Expand Down
38 changes: 38 additions & 0 deletions contrib/codeql/lib/types.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2026-present, The Dash Core developers
* SPDX-License-Identifier: MIT
* See the accompanying file LICENSE or https://opensource.org/license/MIT
*
* @description Helpers for reading and resolving written type positions.
*/

import rust
private import codeql.rust.internal.typeinference.Type as T
private import codeql.rust.internal.typeinference.TypeMention

/** Gets the head identifier of `tr`, e.g. `Vec` for `Vec<u8>`. */
string typeHead(TypeRepr tr) {
result = tr.(PathTypeRepr).getPath().getSegment().getIdentifier().getText()
}

/** Gets the type item `tr` names, resolved through the type layer. */
TypeItem namedTypeItem(TypeRepr tr) {
result = tr.(TypeMention).getType().(T::DataType).getTypeItem()
}

/** Gets the declared type of a field of `t`, including enum variant fields. */
TypeRepr fieldTypeRepr(TypeItem t) {
result = t.(Struct).getFieldList().(StructFieldList).getAField().getTypeRepr()
or
result = t.(Struct).getFieldList().(TupleFieldList).getField(_).getTypeRepr()
or
result = t.(Union).getStructFieldList().getAField().getTypeRepr()
or
exists(Variant v |
v = t.(Enum).getVariantList().getAVariant() and
(
result = v.getFieldList().(StructFieldList).getAField().getTypeRepr() or
result = v.getFieldList().(TupleFieldList).getField(_).getTypeRepr()
)
)
}
Loading
Loading