LLMSkillHub
mirroredCopied from PlamenTSV/plamen under MIT. PlamenTSV has not claimed this page, and is not affiliated with LLM SkillHub.

plamentsv / cid-capability-safety

Trigger Pattern Always required for DAML audits - Inject Into Breadth agents, depth-external, depth-edge-case

0
SKILL.md
6.0 KB

CIDCAPABILITYSAFETY Skill (DAML)

Trigger Pattern: Always required for DAML audits
Inject Into: Breadth agents, depth-external, depth-edge-case
Finding prefix: [DML-CC-N]
Rules referenced: R4, R8, R12

A ContractId t is a DAML-distinctive capability: holding one lets a party fetch/exercise the referenced contract. When a choice accepts a ContractId as an argument, that CID is attacker-chosen unless the body re-binds it to the operation. The four high-yield bug shapes: (1) caller-supplied CID not bound to the operation (caller passes a lookalike), (2) a config/whitelist CID that is missing → the choice fails open, (3) a hardcoded/stale config CID that has been archived → the choice bricks, (4) wrong-CID / type confusion via unchecked fromInterfaceContractId coercion.

1. Caller-Supplied CID Inventory

For every choice that takes a ContractId argument, determine whether the body binds it to the operation:

Template.ChoiceCID ArgFetched/Exercised?Bound To Operation?Binding CheckFinding?
{T.C}cid : ContractId T2YES/NOYES/NOassertMsg ... (fetched.owner == this.owner) / NONE[DML-CC-N] if unbound

Critical patterns to flag:

  • A choice that fetch cid and then acts on the fetched contract's fields without asserting the fetched contract relates to this (e.g., same owner, same asset, expected template) — caller passes any CID they can see ([ELEVATE:CID_BINDING]).
  • A choice that exercises a caller-supplied CID to move value, with no check that the CID is the intended counterparty contract.

DAML note: fetch cid succeeds for ANY contract the exercising party is a stakeholder of (or that is divulged). There is no implicit "this is the right contract" guarantee — the body MUST assert the relationship.

2. Config / Whitelist CID Fail-Open

A choice that looks up a config or whitelist contract by CID and proceeds when it is absent fails open.

Template.ChoiceConfig/Whitelist CID SourceAbsent-HandlingFail-Open?Finding?
{T.C}arg / template field / keyproceeds / errors / default-permitYES/NO[DML-CC-N] if fail-open

Attack: A choice intended to enforce a whitelist fetches a Whitelist contract and checks membership, but if the whitelist CID is not provided (or lookupByKey returns None), the code path skips the check and permits the action ([ELEVATE:FAIL_OPEN]). Verify the absent/None case DENIES, not permits.

3. Hardcoded / Stale Config CID Brick

A CID stored in a template field (or hardcoded) that points to a contract which can be archived creates a brick.

Template.FieldHolds A CID?Referenced Contract Archivable?Update Path For The CID?Brick Risk?
{T.f}YES/NOYES/NOYES/NO[DML-CC-N] if no update path

Attack: A long-lived contract stores a ContractId Config field. The referenced Config is archived/superseded; every choice that fetches the stale CID now gets CONTRACT_NOT_FOUND and bricks, with no choice to update the field ([ELEVATE:CID_BRICK]). Verify either the referenced contract is non-archivable or there is an authorized update choice.

4. Wrong-CID / Type Confusion (interface coercion)

fromInterfaceContractId and coerceContractId reinterpret a CID as a different template/interface without runtime proof.

SiteCoercionSource CID Verified As Target Type?fetch-after-coerce Guard?Finding?
{choice:line}fromInterfaceContractId @T cidYES/NOYES/NO[DML-CC-N] if unverified

Attack: A choice coerces an interface CID to a concrete template via fromInterfaceContractId @T and immediately acts, but the underlying contract is a different template implementing the same interface. The subsequent fetch may succeed against an unintended template, or the choice trusts fields that mean something different. Verify a fetch after coercion (which fails for the wrong template) OR an explicit type/field assertion guards the coercion.

Finding Template

**ID**: [DML-CC-N]
**Severity**: [Critical if value moved on forged CID, High if fail-open/brick, Medium if type-confusion needs setup]
**Step Execution**: ✓1,2,3,4 | ✗(reasons) | ?(uncertain)
**Rules Applied**: [R4:✓/✗, R8:✓/✗, R12:✓/✗]
**Location**: {Module}.daml:LineN (template X, choice Y)
**Title**: Caller-supplied ContractId unbound / fail-open config CID in {Choice} allows {forged-target / bypass / brick}
**Description**: [The CID argument or config CID, the missing binding/absent-handling, and the contract the attacker substitutes]
**Impact**: [Action against attacker-chosen contract / whitelist bypass / permanent brick / type-confused field read]
**PoC steer**: pass a lookalike CID the attacker created and assert harm; or archive the config contract and assert the brick (CONTRACT_NOT_FOUND); or omit the whitelist CID and assert the gated action still succeeds.

Step Execution Checklist (MANDATORY)

SectionRequiredCompleted?Notes
1. Caller-Supplied CID InventoryYES✓/✗/?Every choice taking a ContractId arg
2. Config / Whitelist CID Fail-OpenIF config/whitelist CID lookups present✓/✗(N/A)/?Verify absent → deny
3. Hardcoded / Stale Config CID BrickIF a template field holds a CID✓/✗(N/A)/?Verify update path exists
4. Wrong-CID / Type ConfusionIF fromInterfaceContractId/coerceContractId used✓/✗(N/A)/?Verify coercion is guarded