# unprotected-value-transfer

> Successful token operations have no required application predicate constraining private caller inputs.

Canonical: https://nightshade.dicksonwu.xyz/rules/unprotected-value-transfer.html
Release: Nightshade 0.1.1 (v0.1.1)

| Field | Value |
| --- | --- |
| Rule ID | `unprotected-value-transfer` |
| Severity | low |
| Confidence | low |
| Maturity | experimental |
| Selection | Opt-in |
| Policy intent | not required |
| Default enabled | no |
| Category | Other active opt-ins / Token, coin and value transfers |
| Engine facts used | successful-predicates, all-predicates, runtime-operations, completed-entries |
| Release | v0.1.1 |

## Selection and scope

Not selected by default. Select it with `--detectors` or the `detectors` list in nightshade.json. Either one replaces profile selection, so list every rule you want.

```sh
./.nightshade/node_modules/.bin/nightshade ./contracts --detectors unprotected-value-transfer
./.nightshade/node_modules/.bin/nightshade detector show unprotected-value-transfer
```

The 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 |
| ---: | ---: | ---: | ---: | ---: |
| 2/2 | 2/2 | 2/2 | 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](https://nightshade.dicksonwu.xyz/evaluation.md#synthetic-corpus).

## Rule documentation

From https://github.com/DicksonWu654/nightshade/blob/v0.1.1/docs/detectors/unprotected-value-transfer.md at v0.1.1.

Availability: **opt-in** with `--detectors unprotected-value-transfer`; no policy
required. Severity **low**, confidence **low**. Explicit selection replaces the
default detector set.

Owned example: the compiler-qualified [fixture](#example-1)
exposes a canonical send without a required private caller predicate. Its
[correction](#example-2) checks a
private-key hash against stored owner data. Review whether the transfer is meant
to be restricted; recommendation: enforce the intended caller/protocol check
where required, and preserve documented public faucets or forwarding behavior.

The requested rule reviews successful exported invocations of the canonical
Compact standard-library `mintShieldedToken`, `mintUnshieldedToken`, `sendShielded`,
`sendUnshielded` and `sendImmediateShielded` when no required caller-dependent
check constrains the operation. It must identify actual operation reaches, including
nested calls and replay; a user-defined helper with the same name is not a token
operation. Constructors are outside the exported-operation claim.

A condition merely mentioning a private value is insufficient. A branch with both
arms reaching the same operation must not manufacture a required guard. Conversely,
alternate real authentication in different arms and a late assertion constraining
atomic success must remain valid. Successful predicates need full private-input
scope; witness-only facts omit parameter checks. Effects and constraints must remain
on the same history, and exhausted analysis must stay explicitly incomplete.

An unguarded public faucet or mint can be intentional. This rule cannot infer that
transfers should be private or privileged, or verify arbitrary cryptographic or
protocol checks.

### Evidence query and limits

Actual kernel/native histories identify canonical token helper invocations. An
exported entry and operation form one group; nested invocation chains remain in the
finding. Internal `sendShielded` calls inside `sendImmediateShielded` do not create
a duplicate operation finding.

Within each identical recorded public context, the bounded predicate-cover query
checks whether the union of successful histories covers every Boolean valuation
of their recorded private conditions. This cancels irrelevant reconverging branch
choices while retaining an assertion, disjunction, branch-only transfer or
alternative checks. Repeated witness invocations remain distinct atoms. The query
reads completed histories; it does not execute new paths, solve numeric conditions
or infer cryptographic validity. It excludes pinned stdlib recipient/change branches
by canonical function identity, not source names. Application reuses of those
conditions remain visible even when prior assumptions have refined them to constants.
Literal truths and immutable self-equalities supply no constraint. Missing expression
identity is opaque evidence, never an invented equality between unrelated values.

The result means **no required predicate in this recorded application evidence**.
Implicit overflow/cast/transaction checks still apply. The rule does not prove path
feasibility or intended authorization. Arbitrary algebraic relationships between
atoms, distinct but equivalent public contexts and opaque predicates can prevent
a report. Private dependency does not certify a check's strength as authentication.
The `spoofable-authorization` rule separately reviews raw witness equalities.

Observation uses scope `all` and the existing engine value budget. Applicability
is bounded to 20,000 graph operations; each cover query to 100,000 operations/depth
64 and condition keys to 65,536 characters before and after composition; total rule
work to 1,000,000 operations. Missing invocation/predicate evidence,
unsupported effects and exhaustion remain explicitly incomplete. A failed cover
query returns no partial rule findings. Existing correlated/Counter domains do not
execute these token operations and may certify their structural absence; their
admission scope is unchanged.

### Usage, example and correction

```sh
node dist/src/cli.js scan examples/review/contract.compact \
  --detectors unprotected-value-transfer
```

Explicit `--detectors` selects that set rather than adding to defaults. Include
other desired IDs in the comma-separated selection. The CLI marks this rule as
`opt-in review`; JSON detector metadata has `defaultEnabled: false`.

```compact
import CompactStandardLibrary;
export circuit withdraw(recipient: Either<ContractAddress, UserAddress>): [] {
  sendUnshielded(default<Bytes<32>>, 1, disclose(recipient));
}
```

This is a review match when selected. If withdrawal should be restricted, enforce
the intended caller or protocol check. The corrected corpus fixture asserts a
private-key hash against stored owner data before sending. Public faucets and
payment/forwarding flows should retain their intended policy; an opt-in match alone
is not a reason to add an owner restriction. User-defined namesake helpers are quiet.

### Source examples

These short regression examples are included here for installed-package readers.
Rule-specific policy requirements and limits above still apply.

#### Example 1

Fixture (source: `tests/corpus/vulnerable/unprotected-value-transfer.compact`).

```compact
import CompactStandardLibrary;
export circuit withdraw(recipient: Either<ContractAddress, UserAddress>): [] {
  sendUnshielded(default<Bytes<32>>, 1, disclose(recipient));
}
```

#### Example 2

Correction (source: `tests/corpus/safe/unprotected-value-transfer.compact`).

```compact
import CompactStandardLibrary;
export ledger owner: Bytes<32>;
witness secret(): Bytes<32>;
export circuit withdraw(recipient: Either<ContractAddress, UserAddress>): [] {
  assert(persistentHash<Bytes<32>>(secret()) == owner, "owner");
  sendUnshielded(default<Bytes<32>>, 1, disclose(recipient));
}
```

## 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:

```compact
// nightshade-disable-next-line unprotected-value-transfer -- Reason this match is intentional
```

Diagnostics cannot be suppressed, and an unmatched directive for a selected rule makes analysis incomplete. See [configuration](https://nightshade.dicksonwu.xyz/configuration.md#suppress-an-intentional-match) for fingerprint suppressions and baselines.

## References

- [Rule source document at v0.1.1](https://github.com/DicksonWu654/nightshade/blob/v0.1.1/docs/detectors/unprotected-value-transfer.md)
- [Catalog entry in rules.json](https://nightshade.dicksonwu.xyz/rules.json)
- [All rules](https://nightshade.dicksonwu.xyz/rules.md#token-coin-and-value-transfers)
- [Limitations](https://nightshade.dicksonwu.xyz/limitations.md)
