Rule
unsupported-contract-owner
A declared callable pinned Ownable instance finishes with a contract-address owner rejected by its guard.
Specimen 43 of 96 · Policy-dependent rules
- Severity
- medium
- Confidence
- high
- Maturity
- experimental
- Selection
- Opt-in + policy
- Policy intent
- required
- Release
- v0.1.1
- Engine facts used
module-identitiesstate-writesall-predicatesconstantscompleted-entriescallable-ownable-policy
Selection and scope#
Not selected by default. Select it with --detectors or the detectors list in nightshade.json, and declare the matching application intent in policies. Selecting it alone does not supply that intent.
./.nightshade/node_modules/.bin/nightshade ./contracts --detectors unsupported-contract-owner
./.nightshade/node_modules/.bin/nightshade detector show unsupported-contract-ownerThe first command runs only this rule; --detectors takes a comma-separated list. The second prints the rule's metadata from your installed version.
Severity is review priority. It does not express exploitability or impact. Confidence is detector metadata, not a probability. All built-in rules are experimental.
Compiler scope: the engine supports the pinned profiles 0.28.0, 0.29.0, 0.30.0, 0.31.1, 0.33.0-rc.2, 0.34.0, 0.35.0-rc.1 and 0.35.0, with ZKIR2 by default. Support for this rule can be narrower; the rule documentation below states any narrower scope.
Corpus checks#
Synthetic corpus checks for this rule in the 0.1.1 proof run. A check counts only with compiler acceptance and complete analysis.
| Positive detected | Corrected quiet | Lookalike quiet | Other negative quiet | Incomplete |
|---|---|---|---|---|
| 1/1 | 1/1 | 1/1 | 0/0 | 0 |
No deviations were recorded for this rule.
These are behavior checks on synthetic fixtures, not a precision or recall estimate. See evaluation.
Rule documentation#
From docs/detectors/unsupported-contract-owner.md at v0.1.1.
Availability: opt-in policy; severity medium, confidence high for the final selected tag and authenticated guard behavior. Concrete feasibility remains unproved.
Description, example and scenario#
Ownable__unsafeTransferOwnership(right<Bytes<32>, ContractAddress>(next));For an owner selected by callableOwnable, a complete modeled successful history leaves the pinned Ownable owner's active arm as ContractAddress. Its assertOnlyOwner implementation explicitly rejects that arm, including nonzero contract addresses, while this owner value remains stored. The library's unsafe API is intentional; the policy declares that this particular owner guard must remain usable with the current implementation.
Evidence uses OpenZeppelin Compact 0.4.0-alpha.3 revision ec1331cd8195694c747d291e8fc8ab6d2914a976, exact Ownable and Utils bytes, and the qualified Compact 0.34.0/ZKIR2 profile. The rule reviews configured application invariants; it does not claim an upstream defect or a newly confirmed vulnerability.
Configuration and recommendation#
Select unsupported-contract-owner explicitly and declare:
{ "callableOwnable": [{ "owner": "_owner" }] }The selector must resolve to a root native Either<Bytes<32>, ContractAddress> ledger. Use its exact symbol ID when multiple instances make _owner ambiguous. Arrays must be dense and contain at most 64 owners; duplicate selectors for the same resolved owner are deduplicated. Authentication failures remain rule-scoped incomplete rather than quietly passing the declared obligation.
Keep the selected owner in the account-identifier arm and use the safe ownership-transfer circuit. Intentional authority suspension, migration staging for future C2C support and test exposures should remain outside this policy. Declaring a policy without selecting this opt-in rule does not run the rule.
Evidence and limitations#
Ordered state writes establish the final surviving discriminator for each selected instance and successful history. Whole-owner writes and aggregate reconstructions are compiler-qualified, including constructor and direct authenticated ledger assignments. Exact regional discriminator/payload facts are handled separately; their controls are synthetic because the pinned compiler rejects direct native Either field assignment. Payload-only mutations preserve the discriminator. A later left discriminator, safe replacement or canonical left/zero renunciation stays quiet. Rejected histories cannot supply a finding.
Bounded successful-constant projection uses the same immutable snapshot and history, including later atomic assertions. It cannot borrow constraints from a different ledger revision, branch, wildcard region or module instance. Unknown final tags, unknown overlapping regions, absent invocation locations, incomplete entries, unsupported profiles and exhausted observations produce rule-scoped incomplete diagnostics. Supported findings for independent relations and histories remain available.
The real-source seed triple is an owned focused mutation of the pinned OZ MockOwnable test wrapper. Its nonzero right owner is independent of the existing default-address rule; corrected and unrelated-ledger controls remain quiet.
Suppress a match#
If a match is intentional, suppress it on the line before with the exact rule ID and a reason of at least eight characters:
// nightshade-disable-next-line unsupported-contract-owner -- Reason this match is intentionalDiagnostics cannot be suppressed, and an unmatched directive for a selected rule makes analysis incomplete. See configuration for fingerprint suppressions and baselines.