multiversx-security-audit

Complete security audit methodology for MultiversX smart contracts. Covers context building, entry point analysis, static analysis patterns, and automated Semgrep scanning. Use when performing security audits, code reviews, or setting up automated vulnerability detection.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "multiversx-security-audit" with this command: npx skills add multiversx/mx-ai-skills/multiversx-mx-ai-skills-multiversx-security-audit

MultiversX Security Audit Methodology

A sequential workflow for auditing MultiversX smart contracts, from initial reconnaissance through automated scanning.

When to Use

  • Starting a new security audit engagement
  • Performing security code reviews
  • Setting up automated vulnerability scanning
  • Mapping attack surface for penetration testing
  • Training new security reviewers

Phase 1: Context Building

Rapidly build a comprehensive mental model of the codebase before diving into vulnerability hunting.

1.1 Reconnaissance Checklist

  • Core logic: #[multiversx_sc::contract], #[payable], value-moving functions, access control patterns
  • Externalities: cross-contract calls, hardcoded addresses (sc: literals), oracle dependencies, bridge contracts
  • Documentation: README.md, specs/, sc-config.toml, multiversx.yaml, snippets.sh, scenarios/ tests

1.2 System Mapping

Roles and Permissions

RoleCapabilitiesHow Assigned
OwnerFull admin accessDeploy-time, transferable
AdminLimited admin functionsOwner grants
UserPublic endpointsAnyone
WhitelistedSpecial accessAdmin grants

Asset Inventory

Asset TypeExamplesRisk Level
EGLDNative currencyCritical
Fungible ESDTCustom tokensHigh
NFT/SFTNon-fungible tokensMedium-High
Meta-ESDTTokens with metadataMedium-High

State Analysis

Document all storage mappers and their purposes:

// Example state inventory
#[storage_mapper("owner")]           // SingleValueMapper - access control
#[storage_mapper("balances")]        // MapMapper - user funds (CRITICAL)
#[storage_mapper("whitelist")]       // SetMapper - privileged users

1.3 Entry Point Enumeration (Initial)

List all #[endpoint] functions with their risk classification:

HIGH RISK:
- deposit() - #[payable("*")] - accepts value
- withdraw() - moves funds out
- upgrade() - can change contract logic

MEDIUM RISK:
- setConfig() - owner only, changes behavior
- addWhitelist() - expands permissions

LOW RISK:
- getBalance() - #[view] - read only

1.4 Threat Modeling (Initial)

Asset at Risk Analysis

  • Direct Loss: What funds can be stolen if the contract fails?
  • Indirect Loss: What downstream systems depend on this contract?
  • Reputation Loss: What non-financial damage could occur?

Attacker Profiles

AttackerMotivationCapabilities
External UserProfitPublic endpoints only
Malicious AdminInsider threatAdmin functions
Reentrant ContractExploit callbacksCross-contract calls
Front-runnerMEV extractionTransaction ordering

1.4 Environment Check

Dependency Audit

  • Framework Version: Check Cargo.toml for multiversx-sc version
  • Known Vulnerabilities: Compare against security advisories
  • Deprecated APIs: Look for usage of deprecated functions

Test Suite Assessment

  • Coverage: Does scenarios/ exist with comprehensive tests?
  • Edge Cases: Are failure paths tested?
  • Freshness: Run sc-meta test-gen to verify tests match current code

1.5 Context Building Output

After completing context building, document:

  1. System Overview: One-paragraph summary of what the contract does
  2. Trust Boundaries: Who trusts whom, what assumptions exist
  3. Critical Paths: The most security-sensitive code paths
  4. Initial Concerns: Preliminary list of areas requiring deep review
  5. Questions for Team: Clarifications needed from developers

Context Building Checklist

  • All entry points identified and classified
  • Storage layout documented
  • External dependencies mapped
  • Role/permission model understood
  • Test coverage assessed
  • Framework version noted
  • Initial threat model drafted

Phase 2: Entry Point Analysis

Identify the complete attack surface by enumerating all public interaction points and classifying their risk levels.

2.1 Entry Point Identification

MultiversX Macros That Expose Functions

MacroVisibilityRisk LevelDescription
#[endpoint]Public writeHighState-changing public function
#[view]Public readLowRead-only public function
#[payable]Accepts any tokenCriticalHandles value transfers
#[payable("EGLD")]Accepts EGLD onlyCriticalHandles native currency
#[init]Deploy onlyMediumConstructor (runs once)
#[upgrade]Upgrade onlyCriticalMigration logic
#[callback]InternalHighAsync call response handler
#[only_owner]Owner restrictedMediumAdmin functions

Scanning Commands

# Find all endpoints
grep -n "#\[endpoint" src/*.rs

# Find all payable endpoints
grep -n "#\[payable" src/*.rs

# Find all views
grep -n "#\[view" src/*.rs

# Find callbacks
grep -n "#\[callback" src/*.rs

# Find init and upgrade
grep -n "#\[init\]\|#\[upgrade\]" src/*.rs

2.2 Risk Classification

Category A: Payable Endpoints (Critical Risk)

Functions receiving value require the most scrutiny.

#[payable]
#[endpoint]
fn deposit(&self) {
    // MUST CHECK:
    // 1. Token identifier validation
    // 2. Amount > 0 validation
    // 3. Correct handling of multi-token transfers
    // 4. State updates before external calls

    let payment = self.call_value().single();
    require!(
        payment.token_identifier == self.accepted_token().get(),
        "Wrong token"
    );

    // Process deposit...
}

Checklist for Payable Endpoints:

  • Token ID validated against expected token(s)
  • Amount checked for minimum/maximum bounds
  • Multi-transfer handling if all() used
  • Nonce validation for NFT/SFT
  • Reentrancy protection (Checks-Effects-Interactions)

Category B: Non-Payable State-Changing Endpoints (High Risk)

#[endpoint]
fn update_config(&self, new_value: BigUint) {
    // MUST CHECK:
    // 1. Who can call this? (access control)
    // 2. Input validation
    // 3. State transition validity

    self.require_caller_is_admin();
    require!(new_value > 0, "Invalid value");
    self.config().set(new_value);
}

Checklist for State-Changing Endpoints:

  • Access control implemented and correct
  • Input validation for all parameters
  • State transitions are valid
  • Events emitted for important changes
  • No DoS vectors (unbounded loops, etc.)

Category C: View Functions (Low Risk)

#[view(getBalance)]
fn get_balance(&self, user: ManagedAddress) -> BigUint {
    // SHOULD CHECK:
    // 1. Does it actually modify state? (interior mutability)
    // 2. Does it leak sensitive information?
    // 3. Is the calculation expensive (DoS via gas)?

    self.balances(&user).get()
}

Checklist for View Functions:

  • No state modification (verify no storage writes)
  • No sensitive data exposure
  • Bounded computation (no unbounded loops)
  • Block info usage appropriate (get_block_timestamp_millis() / get_block_timestamp_seconds() may differ off-chain)

Category D: Init and Upgrade (Critical Risk)

#[init]
fn init(&self, admin: ManagedAddress) {
    // MUST CHECK:
    // 1. All required state initialized
    // 2. No way to re-initialize
    // 3. Admin/owner properly set

    self.admin().set(admin);
}

#[upgrade]
fn upgrade(&self) {
    // MUST CHECK:
    // 1. New storage mappers initialized
    // 2. Storage layout compatibility
    // 3. Migration logic correct
}

Category E: Callbacks (High Risk)

#[callback]
fn transfer_callback(
    &self,
    #[call_result] result: ManagedAsyncCallResult<()>
) {
    // MUST CHECK:
    // 1. Error handling (don't assume success)
    // 2. State reversion on failure
    // 3. Correct identification of original call

    match result {
        ManagedAsyncCallResult::Ok(_) => {
            // Success path
        },
        ManagedAsyncCallResult::Err(_) => {
            // CRITICAL: Must handle failure!
            // Revert any state changes from original call
        }
    }
}

2.3 Analysis Workflow

Step 1: List All Entry Points

| Endpoint | Type | Payable | Access | Storage Touched | Risk |
|----------|------|---------|--------|-----------------|------|
| deposit | endpoint | * | Public | balances | Critical |
| withdraw | endpoint | No | Public | balances | Critical |
| setAdmin | endpoint | No | Owner | admin | High |
| getBalance | view | No | Public | balances (read) | Low |
| init | init | No | Deploy | admin, config | Medium |

Step 2: Tag Access Control

// Public - anyone can call
#[endpoint]
fn public_function(&self) { }

// Owner only - blockchain owner
#[only_owner]
#[endpoint]
fn owner_function(&self) { }

// Admin only - custom access control
#[endpoint]
fn admin_function(&self) {
    self.require_caller_is_admin();
}

// Whitelisted - address in set
#[endpoint]
fn whitelist_function(&self) {
    let caller = self.blockchain().get_caller();
    require!(self.whitelist().contains(&caller), "Not whitelisted");
}

Step 3: Tag Value Handling

TagMeaningExample
RefusableRejects paymentsDefault (no #[payable])
EGLD OnlyAccepts EGLD#[payable("EGLD")]
Token OnlySpecific ESDT#[payable("TOKEN-abc123")]
Any TokenAny payment#[payable]
Multi-TokenMultiple paymentsUses all()

Step 4: Graph Data Flow

deposit() ──writes──▶ balances
          ──writes──▶ total_deposited
          ──reads───▶ accepted_token

withdraw() ──reads/writes──▶ balances
           ──reads────────▶ withdrawal_fee

getBalance() ──reads──▶ balances

2.4 Specific Attack Vectors

Privilege Escalation

// VULNERABLE: Missing access control
#[endpoint]
fn set_admin(&self, new_admin: ManagedAddress) {
    self.admin().set(new_admin);  // Anyone can become admin!
}

// CORRECT: Protected
#[only_owner]
#[endpoint]
fn set_admin(&self, new_admin: ManagedAddress) {
    self.admin().set(new_admin);
}

DoS via Unbounded Growth

// VULNERABLE: Public endpoint adds to unbounded set
#[endpoint]
fn register(&self) {
    let caller = self.blockchain().get_caller();
    self.participants().insert(caller);  // Grows forever!
}

Missing Payment Validation

Bad:

// DON'T: Accept any token without validation — attacker sends worthless tokens
#[payable]
#[endpoint]
fn stake(&self) {
    let payment = self.call_value().single();
    self.staked().update(|s| *s += payment.amount.as_big_uint()); // Fake tokens accepted!
}

Good:

// DO: Validate token identity and optionally enforce minimum amount
#[payable]
#[endpoint]
fn stake(&self) {
    let payment = self.call_value().single();
    require!(
        payment.token_identifier == self.staking_token().get(),
        "Wrong token"
    );
    self.staked().update(|s| *s += payment.amount.as_big_uint());
}

Callback State Assumptions

// VULNERABLE: Assumes success
#[callback]
fn on_transfer_complete(&self) {
    // This runs even if transfer FAILED!
    self.transfer_count().update(|c| *c += 1);
}

2.5 Entry Point Analysis Output Template

# Entry Point Analysis: [Contract Name]

## Summary
- Total Endpoints: X
- Payable Endpoints: Y (Critical)
- State-Changing: Z (High)
- Views: W (Low)

## Access Control Matrix

| Endpoint | Public | Owner | Admin | Whitelist |
|----------|--------|-------|-------|-----------|
| deposit | Yes | - | - | - |
| setAdmin | - | Yes | - | - |

## Recommended Focus Areas
1. [Highest priority endpoint and why]
2. [Second priority]
3. [Third priority]

Phase 3: Static Analysis Patterns

Manual and automated static analysis for finding vulnerabilities in MultiversX Rust and Go code.

3.1 Rust Smart Contracts (multiversx-sc)

Critical Grep Patterns

Unsafe Code
# Unsafe blocks - valid only for FFI or specific optimizations
grep -rn "unsafe" src/

Risk: Memory corruption, undefined behavior Action: Require justification for each unsafe block

Panic Inducers
# Direct unwrap - can panic
grep -rn "\.unwrap()" src/

# Expect - also panics
grep -rn "\.expect(" src/

# Index access - can panic on out of bounds
grep -rn "\[.*\]" src/ | grep -v "storage_mapper"

Risk: Contract halts, potential DoS Action: Replace with unwrap_or_else(|| sc_panic!(...)) or proper error handling

Floating Point Arithmetic
grep -rn "f32" src/
grep -rn "f64" src/
grep -rn "as f32\|as f64" src/

Risk: Non-deterministic behavior, consensus failure Action: Use BigUint/BigInt for all calculations

Unchecked Arithmetic
grep -rn "[^_a-zA-Z]\+ [^_a-zA-Z]" src/  # Addition
grep -rn "[^_a-zA-Z]\- [^_a-zA-Z]" src/  # Subtraction
grep -rn "[^_a-zA-Z]\* [^_a-zA-Z]" src/  # Multiplication

Risk: Integer overflow/underflow Action: Use BigUint or checked arithmetic for all financial calculations

Map Iteration (DoS Risk)
grep -rn "\.iter()" src/
grep -rn "for.*in.*\.iter()" src/
grep -rn "\.collect()" src/

Risk: Gas exhaustion DoS Action: Add pagination or bounds checking

Logical Pattern Analysis (Manual Review)

Token ID Validation
grep -rn "call_value()" src/
grep -rn "\.single()" src/
grep -rn "\.all()" src/
grep -rn "\.array()" src/
grep -rn "\.single_optional()" src/
# Legacy patterns (may still be in older code)
grep -rn "all_esdt_transfers" src/
grep -rn "single_esdt" src/

For each occurrence, verify:

  • Token ID checked against expected value (using TokenId comparison)
  • Token nonce validated (for NFT/SFT)
  • Amount validated (within bounds — non-zero is guaranteed by NonZeroBigUint in Payment)
// VULNERABLE
#[payable]
fn deposit(&self) {
    let payment = self.call_value().single();
    self.balances().update(|b| *b += payment.amount.as_big_uint());
    // No token ID check! Accepts any token
}

// SECURE
#[payable]
fn deposit(&self) {
    let payment = self.call_value().single();
    require!(
        payment.token_identifier == self.accepted_token().get(),
        "Wrong token"
    );
    // Note: amount > 0 check is no longer needed — Payment.amount is NonZeroBigUint
    self.balances().update(|b| *b += payment.amount.as_big_uint());
}
Callback State Assumptions
grep -rn "#\[callback\]" src/

For each callback, verify:

  • Does NOT assume async call succeeded
  • Handles error case explicitly
  • Reverts state changes on failure if needed
Access Control
grep -rn "#\[endpoint\]" src/
grep -rn "#\[only_owner\]" src/

For each endpoint, verify:

  • Appropriate access control applied
  • Sensitive operations restricted
  • Admin functions documented
Reentrancy (CEI Pattern)
grep -rn "\.send()\." src/
grep -rn "\.tx()" src/
grep -rn "async_call" src/

Verify Checks-Effects-Interactions pattern:

  • All checks (require!) before state changes
  • State changes before external calls
  • No state changes after external calls in same function

3.2 Go Protocol Code (mx-chain-go)

Concurrency Issues

Goroutine Loop Variable Capture
grep -rn "go func" *.go
// VULNERABLE
for _, item := range items {
    go func() {
        process(item)  // item may have changed!
    }()
}

// SECURE
for _, item := range items {
    item := item  // Create local copy
    go func() {
        process(item)
    }()
}
Map Race Conditions
grep -rn "map\[" *.go | grep -v "sync.Map"

Determinism Issues

Map Iteration Order
grep -rn "for.*range.*map" *.go

Map iteration in Go is random. Never use for generating hashes, creating consensus data, or any deterministic output.

Time Functions
grep -rn "time.Now()" *.go

time.Now() is forbidden in block processing — use block.Header.TimeStamp instead.

3.3 Analysis Checklist

Smart Contract Review Checklist

Access Control

  • All endpoints have appropriate access restrictions
  • Owner/admin functions use #[only_owner] or explicit checks
  • No privilege escalation paths

Payment Handling

  • Token IDs validated in all #[payable] endpoints
  • Amounts validated (non-zero, bounds)
  • NFT nonces validated where applicable

Arithmetic

  • No raw arithmetic on u64/i64 with external inputs
  • BigUint used for financial calculations
  • No floating point

State Management

  • Checks-Effects-Interactions pattern followed
  • Callbacks handle failure cases
  • Storage layout upgrade-safe

Gas & DoS

  • No unbounded iterations
  • Storage growth is bounded
  • Pagination for large data sets

Error Handling

  • No unwrap() without justification
  • Meaningful error messages
  • Consistent error handling patterns

Protocol Review Checklist

Concurrency

  • All shared state properly synchronized
  • No goroutine loop variable capture bugs
  • Channel usage is correct

Determinism

  • No map iteration for consensus data
  • No time.Now() in block processing
  • No random number generation without deterministic seed

Memory Safety

  • Bounds checking on slices
  • No nil pointer dereferences
  • Proper error handling

3.4 Vulnerability Categories Quick Reference

CategoryGrep PatternSeverity
Unsafe codeunsafeCritical
Float arithmeticf32|f64Critical
Panic inducersunwrap()|expect(High
Unbounded iteration\.iter()High
Missing access control#[endpoint] without #[only_owner]High
Token validationcall_value().single() without token ID requireHigh
Callback assumptions#[callback] without error handlingMedium
Raw arithmetic+ | - | * on u64Medium

Phase 4: Automated Scanning with Semgrep

Create custom Semgrep rules to automatically detect MultiversX-specific security patterns and best practice violations.

4.1 Semgrep Basics for Rust

Rule Structure

rules:
  - id: rule-identifier
    languages: [rust]
    message: "Description of the issue and why it matters"
    severity: ERROR  # ERROR, WARNING, INFO
    patterns:
      - pattern: <code pattern to match>
    metadata:
      category: security
      technology:
        - multiversx

Pattern Syntax

SyntaxMeaningExample
$VARAny expression$X + $Y matches a + b
...Zero or more statements{ ... } matches any block
$...VARZero or more argumentsfunc($...ARGS)
<... $X ...>Contains expression<... panic!(...) ...>

4.2 Common MultiversX Rules

Unsafe Arithmetic Detection

rules:
  - id: mvx-unsafe-addition
    languages: [rust]
    message: "Potential arithmetic overflow. Use BigUint or checked arithmetic for financial calculations."
    severity: ERROR
    patterns:
      - pattern: $X + $Y
      - pattern-not: $X.checked_add($Y)
      - pattern-not: BigUint::from($X) + BigUint::from($Y)
    paths:
      include:
        - "*/src/*.rs"
    metadata:
      category: security
      subcategory: arithmetic
      cwe: "CWE-190: Integer Overflow"

  - id: mvx-unsafe-multiplication
    languages: [rust]
    message: "Potential multiplication overflow. Use BigUint or checked_mul."
    severity: ERROR
    patterns:
      - pattern: $X * $Y
      - pattern-not: $X.checked_mul($Y)
      - pattern-not: BigUint::from($X) * BigUint::from($Y)

Floating Point Detection

rules:
  - id: mvx-float-forbidden
    languages: [rust]
    message: "Floating point arithmetic is non-deterministic and forbidden in smart contracts."
    severity: ERROR
    pattern-either:
      - pattern: "let $X: f32 = ..."
      - pattern: "let $X: f64 = ..."
      - pattern: "$X as f32"
      - pattern: "$X as f64"
    metadata:
      category: security
      subcategory: determinism

Payable Endpoint Without Value Check

rules:
  - id: mvx-payable-no-check
    languages: [rust]
    message: "Payable endpoint does not check payment value. Verify token ID and amount."
    severity: WARNING
    patterns:
      - pattern: |
          #[payable]
          #[endpoint]
          fn $FUNC(&self, $...PARAMS) {
              $...BODY
          }
      - pattern-not: |
          #[payable]
          #[endpoint]
          fn $FUNC(&self, $...PARAMS) {
              <... self.call_value() ...>
          }
    metadata:
      category: security
      subcategory: input-validation

Unsafe Unwrap Usage

rules:
  - id: mvx-unsafe-unwrap
    languages: [rust]
    message: "unwrap() can panic. Use unwrap_or_else with sc_panic! or proper error handling."
    severity: ERROR
    patterns:
      - pattern: $EXPR.unwrap()
      - pattern-not-inside: |
          #[test]
          fn $FUNC() { ... }
    fix: "$EXPR.unwrap_or_else(|| sc_panic!(\"Error message\"))"
    metadata:
      category: security
      subcategory: error-handling

Missing Owner Check

rules:
  - id: mvx-sensitive-no-owner-check
    languages: [rust]
    message: "Sensitive operation without owner check. Add #[only_owner] or explicit verification."
    severity: ERROR
    patterns:
      - pattern: |
          #[endpoint]
          fn $FUNC(&self, $...PARAMS) {
              <... self.$MAPPER().set(...) ...>
          }
      - pattern-not: |
          #[only_owner]
          #[endpoint]
          fn $FUNC(&self, $...PARAMS) { ... }
      - pattern-not: |
          #[endpoint]
          fn $FUNC(&self, $...PARAMS) {
              <... self.blockchain().get_owner_address() ...>
          }
      - metavariable-regex:
          metavariable: $MAPPER
          regex: "(admin|owner|config|fee|rate)"

4.3 Advanced Patterns

Callback Without Error Handling

rules:
  - id: mvx-callback-no-error-handling
    languages: [rust]
    message: "Callback does not handle error case. Async call failures will silently proceed."
    severity: ERROR
    patterns:
      - pattern: |
          #[callback]
          fn $FUNC(&self, $...PARAMS) {
              $...BODY
          }
      - pattern-not: |
          #[callback]
          fn $FUNC(&self, #[call_result] $RESULT: ManagedAsyncCallResult<$TYPE>) {
              ...
          }

Unbounded Iteration

rules:
  - id: mvx-unbounded-iteration
    languages: [rust]
    message: "Iterating over storage mapper without bounds. Can cause DoS via gas exhaustion."
    severity: ERROR
    pattern-either:
      - pattern: self.$MAPPER().iter()
      - pattern: |
          for $ITEM in self.$MAPPER().iter() {
              ...
          }
    metadata:
      category: security
      subcategory: dos
      cwe: "CWE-400: Uncontrolled Resource Consumption"

Storage Key Collision Risk

rules:
  - id: mvx-storage-key-short
    languages: [rust]
    message: "Storage key is very short, increasing collision risk. Use descriptive keys."
    severity: WARNING
    patterns:
      - pattern: '#[storage_mapper("$KEY")]'
      - metavariable-regex:
          metavariable: $KEY
          regex: "^.{1,3}$"

Reentrancy Pattern Detection

rules:
  - id: mvx-reentrancy-risk
    languages: [rust]
    message: "External call before state update. Follow Checks-Effects-Interactions pattern."
    severity: ERROR
    patterns:
      - pattern: |
          fn $FUNC(&self, $...PARAMS) {
              ...
              self.send().$SEND_METHOD(...);
              ...
              self.$STORAGE().set(...);
              ...
          }
      - pattern: |
          fn $FUNC(&self, $...PARAMS) {
              ...
              self.tx().to(...).transfer();
              ...
              self.$STORAGE().set(...);
              ...
          }

4.4 Creating Rules from Findings

Workflow

  1. Find a bug manually during audit
  2. Abstract the pattern - what makes this a bug?
  3. Write a Semgrep rule to catch similar issues
  4. Test on the codebase - find all variants
  5. Refine to reduce false positives

Example: From Bug to Rule

Bug Found:

#[endpoint]
fn withdraw(&self, amount: BigUint) {
    let caller = self.blockchain().get_caller();
    self.send().direct_egld(&caller, &amount);  // Sends before balance check!
    self.balances(&caller).update(|b| *b -= &amount);  // Can underflow
}

Rule Created:

rules:
  - id: mvx-withdraw-pattern-unsafe
    languages: [rust]
    message: "Withdrawal sends funds before updating balance. Risk of reentrancy and underflow."
    severity: ERROR
    patterns:
      - pattern: |
          fn $FUNC(&self, $...PARAMS) {
              ...
              self.send().$METHOD(...);
              ...
              self.$BALANCE(...).update(|$B| *$B -= ...);
              ...
          }
      - pattern: |
          fn $FUNC(&self, $...PARAMS) {
              ...
              self.tx().to(...).transfer();
              ...
              self.$BALANCE(...).update(|$B| *$B -= ...);
              ...
          }

4.5 Running Semgrep

# Run single rule
semgrep --config rules/mvx-unsafe-arithmetic.yaml src/

# Run all rules in directory
semgrep --config rules/ src/

# Output JSON for processing
semgrep --config rules/ --json -o results.json src/

# Ignore test files
semgrep --config rules/ --exclude="*_test.rs" --exclude="tests/" src/

CI/CD Integration

# GitHub Actions example
- name: Run Semgrep
  uses: returntocorp/semgrep-action@v1
  with:
    config: >-
      rules/mvx-security.yaml
      rules/mvx-best-practices.yaml

4.6 Rule Library Organization

semgrep-rules/
├── security/
│   ├── mvx-arithmetic.yaml      # Overflow/underflow
│   ├── mvx-access-control.yaml  # Auth issues
│   ├── mvx-reentrancy.yaml      # CEI violations
│   └── mvx-input-validation.yaml
├── best-practices/
│   ├── mvx-storage.yaml         # Storage patterns
│   ├── mvx-gas.yaml             # Gas optimization
│   └── mvx-error-handling.yaml
└── style/
    ├── mvx-naming.yaml          # Naming conventions
    └── mvx-documentation.yaml   # Doc requirements

4.7 Testing Rules

Test File Format

# test/mvx-unsafe-unwrap.test.yaml
rules:
  - id: mvx-unsafe-unwrap
    # ... rule definition ...

# Test cases
test_cases:
  - name: "Should match unwrap"
    code: |
      fn test() {
          let x = some_option.unwrap();
      }
    should_match: true

  - name: "Should not match unwrap_or_else"
    code: |
      fn test() {
          let x = some_option.unwrap_or_else(|| sc_panic!("Error"));
      }
    should_match: false

Running Tests

semgrep --test rules/

4.8 Best Practices for Rule Writing

  1. Start specific, then generalize: Begin with exact pattern, relax constraints carefully
  2. Include fix suggestions: Use fix: field when automated fixes are safe
  3. Document the "why": Message should explain impact, not just what's detected
  4. Include CWE references: Link to standard vulnerability classifications
  5. Test with real codebases: Validate against actual MultiversX projects
  6. Version your rules: Rules evolve as framework APIs change
  7. Categorize by severity: ERROR for security, WARNING for best practices

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

multiversx-dapp-audit

No summary provided by upstream source.

Repository SourceNeeds Review
Security

multiversx-audit-context

No summary provided by upstream source.

Repository SourceNeeds Review
General

multiversx-clarification-expert

No summary provided by upstream source.

Repository SourceNeeds Review