Docs
Checking opcode authorization

Checking opcode authorization

Learn how to audit which opcodes in your contract lack authorization checks.

This tutorial shows you how to use the Opcode Authorization Checker to discover which message opcodes in your contract can be invoked by anyone — and which are properly restricted.

Unlike other TSA checkers that give a binary "vulnerable / not vulnerable" verdict, opcode-info produces a summary table of every opcode's authorization status. This makes it a great first step when auditing an unfamiliar contract.

By the end, you'll know how to:

  • Run the opcode authorization checker on a contract
  • Interpret the per-opcode authorization report
  • Identify and fix missing access control

1. Create a Project With a Vulnerable Contract

We'll build a minimal jetton-minter-style contract that stores admin-only content — but "forget" the authorization check.

Create a Blueprint Project

Before you begin, make sure you meet all the requirements for working with Blueprint. You can find the details in the Blueprint documentation.

Once everything is set up, create a new project by running:

npm create ton@latest

You'll be prompted to enter:

  • Project name — enter OpcodeInfoDemo
  • First contract name — enter SimpleMinter
  • Contract template — choose An empty contract (Tolk)

Enter the generated directory:

cd OpcodeInfoDemo

Implement the Vulnerable Contract

The generated empty contract is located at contracts/simple_minter.tolk.

Open this file and replace its contents with the following:

struct (0x00000004) ChangeMinterContent {
    queryId: uint64
    newContent: cell
}
 
struct Storage {
    adminAddress: address
    content: cell
}
 
fun Storage.load() {
    return Storage.fromCell(contract.getData())
}
 
fun Storage.save(self) {
    contract.setData(self.toCell())
}
 
fun onInternalMessage(in: InMessage) {
    if (in.body.isEmpty()) {
        return;  // accept empty messages (top-up)
    }
 
    val msg = lazy ChangeMinterContent.fromSlice(in.body);
 
    // BUG: no check that in.senderAddress == storage.adminAddress
    var storage = lazy Storage.load();
    storage.content = msg.newContent;
    storage.save();
}

Security issue: The ChangeMinterContent handler updates the on-chain content without verifying that the sender is the admin. Any user can overwrite the content.

2. Set Up the TSA Plugin

Refer to the TSA Blueprint Plugin Installation Guide for instructions.

3. Run the Opcode Authorization Checker

Analyze the contract by running:

yarn blueprint tsa opcode-info -c SimpleMinter

By default, built-in checkers are interactive: TSA asks for confirmation twice — first before preparation and timeout calculation, and then again before the analysis starts. If you want to skip both prompts, add --no-interactive.

The checker performs two phases:

  1. Opcode extraction — Symbolically executes the contract to discover all recognized opcodes.
  2. Authorization analysis — For each opcode, checks whether a random (unauthorized) sender can invoke it and complete the compute phase successfully.

After the analysis finishes, you'll see output similar to:

Extracted opcodes: [0x00000004]

Opcode Authorization Analysis:

0x00000004: ⚠️ No authorization checks
  Path to reproducing input: tsa/reports/run-[id]/typed-input.yaml

The "No authorization checks" warning tells us that anyone can send a message with opcode 0x00000004 and have it processed — exactly the bug we planted.

What the Output Means

The report uses two statuses:

  • Has authorization checks — TSA could not find a way for a random sender to complete this opcode's handler.
  • No authorization checks — A random sender can invoke this opcode and reach a successful compute phase.

The checker doesn't decide whether an unprotected opcode is a vulnerability — some opcodes (like RequestWalletAddress in a real jetton minter) are intentionally public. It's up to you to review the list and verify that every unprotected opcode is meant to be open.

Investigating the Report

When the checker flags an opcode, it generates a reproducing input in the tsa/reports/ directory.

tsa
└── reports
    └── run-[id]
        ├── contract-data.boc
        ├── message-body.boc
        ├── report.sarif
        ├── summary.txt
        └── typed-input.yaml

The raw BoC files contain the exact contract data and message body for the reproducing call. typed-input.yaml combines both of them into a single typed representation, with top-level messageBody and contractData sections.

typed-input.yaml is the most convenient file for investigation — you'll see opcode 0x00000004 followed by the fields TSA chose to complete the call successfully.

4. Fix the Vulnerability and Re-run Analysis

Understanding the Fix

The fix is straightforward: verify the sender's address before modifying state.

The Fixed Contract

Update contracts/simple_minter.tolk:

const ERR_NOT_FROM_ADMIN: int = 73;
 
struct (0x00000004) ChangeMinterContent {
    queryId: uint64
    newContent: cell
}
 
struct Storage {
    adminAddress: address
    content: cell
}
 
fun Storage.load() {
    return Storage.fromCell(contract.getData())
}
 
fun Storage.save(self) {
    contract.setData(self.toCell())
}
 
fun onInternalMessage(in: InMessage) {
    if (in.body.isEmpty()) {
        return;  // accept empty messages (top-up)
    }
 
    val msg = lazy ChangeMinterContent.fromSlice(in.body);
 
    var storage = lazy Storage.load();
    assert(in.senderAddress == storage.adminAddress) throw ERR_NOT_FROM_ADMIN;
    storage.content = msg.newContent;
    storage.save();
}

Verify the Fix

Re-run the checker:

yarn blueprint tsa opcode-info -c SimpleMinter

Expected output:

Extracted opcodes: [0x00000004]

Opcode Authorization Analysis:

0x00000004: ✅ Has authorization checks

The opcode is now properly protected. TSA confirms that a random sender can no longer invoke it successfully.

Conclusion

In this tutorial you've learned how to:

  • Run opcode-info to get a per-opcode authorization summary of your contract
  • Read the authorization report and understand the per-opcode statuses
  • Use the reproducing inputs to investigate flagged opcodes
  • Fix missing access control and verify the fix

The opcode authorization checker is especially useful as an audit starting point: run it once on any contract to get a quick overview of its access control surface, then dig deeper into any surprising results.