Substrate Vulnerability Scanner

Substrate Vulnerability Scanner automation and integration

Substrate Vulnerability Scanner is a community skill for security analysis of Substrate-based blockchain applications, covering pallet auditing, runtime security, storage validation, extrinsic analysis, and vulnerability detection for Polkadot and Kusama ecosystem projects.

What Is This?

Overview

Substrate Vulnerability Scanner provides guidance on identifying security vulnerabilities in Substrate-based blockchain runtimes. It covers pallet auditing that reviews custom pallet logic for common vulnerability patterns, runtime security that validates weight calculations and dispatch logic, storage validation that checks for unbounded storage growth and migration issues, extrinsic analysis that examines transaction handling for fee manipulation and replay risks, and vulnerability detection that identifies arithmetic overflows and access control flaws. The skill helps developers secure Substrate-based chains.

Who Should Use This

This skill serves blockchain developers building on Substrate, security auditors reviewing parachain runtimes, and teams preparing for deployment on Polkadot or Kusama networks.

Why Use It?

Problems It Solves

Custom pallets may contain arithmetic overflows that allow token minting or balance manipulation. Incorrect weight calculations enable denial-of-service through underpriced extrinsics. Unbounded storage vectors grow without limits and increase state bloat. Missing origin checks allow unauthorized calls to privileged functions.

Core Highlights

Pallet auditor scans custom logic for common vulnerability patterns. Weight analyzer validates computational cost estimates. Storage checker identifies unbounded growth risks. Access control validator verifies origin checks on privileged functions.

How to Use It?

Basic Usage

from dataclasses import (
  dataclass)

@dataclass
class Finding:
  severity: str
  category: str
  pallet: str
  description: str
  location: str

class SubstrateScanner:
  def __init__(self):
    self.findings: list[
      Finding] = []

  def check_overflow(
    self, code: str,
    pallet: str
  ):
    patterns = [
      'checked_add',
      'checked_sub',
      'checked_mul',
      'saturating_add']
    lines = code.split(
      '\n')
    for i, line in (
      enumerate(lines)
    ):
      if ('+' in line
        or '-' in line
        or '*' in line
      ):
        has_check = any(
          p in line
          for p in patterns)
        if not has_check:
          self.findings\
            .append(Finding(
              'high',
              'overflow',
              pallet,
              'Unchecked '
              'arithmetic',
              f'line {i+1}'))

  def check_origin(
    self, code: str,
    pallet: str
  ):
    lines = code.split(
      '\n')
    for i, line in (
      enumerate(lines)
    ):
      if 'fn ' in line and (
        'pub' in line
      ):
        if 'ensure_signed' (
          not in code[
            max(0, i-2):
            i+5]
        ):
          self.findings\
            .append(Finding(
              'critical',
              'access',
              pallet,
              'Missing origin'
              ' check',
              f'line {i+1}'))

  def report(self) -> dict:
    by_sev = {}
    for f in self.findings:
      by_sev[f.severity] = (
        by_sev.get(
          f.severity, 0) + 1)
    return {
      'total': len(
        self.findings),
      'by_severity': by_sev}

scanner = SubstrateScanner()
code = 'let total = a + b;'
scanner.check_overflow(
  code, 'balances')
print(scanner.report())

Real-World Examples

from dataclasses import (
  dataclass, field)

@dataclass
class WeightIssue:
  extrinsic: str
  declared: int
  estimated: int
  risk: str

class WeightAnalyzer:
  def __init__(self):
    self.issues: list[
      WeightIssue] = []

  def check_weight(
    self,
    extrinsic: str,
    declared: int,
    estimated: int
  ):
    if estimated > (
      declared * 1.5
    ):
      self.issues.append(
        WeightIssue(
          extrinsic,
          declared,
          estimated,
          'underweight'))
    elif declared > (
      estimated * 3
    ):
      self.issues.append(
        WeightIssue(
          extrinsic,
          declared,
          estimated,
          'overweight'))

  def summary(
    self
  ) -> dict:
    under = len([
      i for i in
      self.issues
      if i.risk == (
        'underweight')])
    over = len([
      i for i in
      self.issues
      if i.risk == (
        'overweight')])
    return {
      'underweight': under,
      'overweight': over,
      'total': len(
        self.issues)}

analyzer = WeightAnalyzer()
analyzer.check_weight(
  'transfer', 10000, 25000)
analyzer.check_weight(
  'stake', 50000, 12000)
print(analyzer.summary())

Advanced Tips

Check all arithmetic operations in pallet logic for checked or saturating variants to prevent overflow exploits. Benchmark actual execution weights against declared weights to find underpriced extrinsics. Review storage migrations for data loss risks when upgrading runtime versions.

When to Use It?

Use Cases

Audit a custom pallet for arithmetic overflow and access control vulnerabilities before deployment. Validate extrinsic weight declarations against benchmarked execution costs. Review storage schemas for unbounded growth that could cause state bloat.

Related Topics

Substrate, Polkadot, blockchain security, pallet auditing, runtime security, smart contract analysis, and parachain development.

Important Notes

Requirements

Access to Substrate pallet source code in Rust for audit review. Understanding of Substrate runtime architecture including pallets, weights, and storage. Familiarity with common blockchain vulnerability patterns.

Usage Recommendations

Do: use checked arithmetic functions for all balance and quantity operations in pallet logic. Benchmark weights using the Substrate frame-benchmarking framework for accurate estimates. Review all public extrinsics for proper origin validation.

Don't: deploy custom pallets without security review since runtime vulnerabilities affect all chain users. Rely solely on automated scanning since manual review catches logic flaws that pattern matching misses. Skip storage migration testing since failed migrations can corrupt chain state.

Limitations

Automated scanning catches common patterns but misses complex logic vulnerabilities. Security analysis requires deep Substrate knowledge that goes beyond general blockchain auditing. Runtime upgrades introduce new attack surfaces that need re-evaluation.