oak_techniques: [OAK-T9.007]
spec_id: oak-detection-T9.007
version: 0.1.0
maturity: stable
maintainer: "@iZonex"
license: Apache-2.0

scope: |
  Detect the structural pattern where Protocol B forks Protocol A's
  codebase, Protocol A discloses or is exploited via a vulnerability,
  and Protocol B leaves the same vulnerability unmitigated until it is
  independently exploited. Detection operates at the fork-relationship-
  mapping and upstream-disclosure-monitoring layer rather than at the
  on-chain exploit-detection layer. The structural signal is: upstream
  exploit disclosed → downstream fork unpatched → exploitation window
  open. Covers the canonical Compound V2 fork cascade (Hundred → Midas →
  Sonne → Onyx → Resupply), Curio/MakerDAO, and PolyBunny/PancakeBunny
  fork chains.

data_sources: [contract_bytecode, security_disclosure_feed,
               dex_trades, contract_storage, contract_events,
               tx_call_trace]

detection_logic:
  description: |
    Three detection paths. PATH A (fork-relationship mapping — highest
    leverage): maintain a live mapping of fork relationships (Protocol A
    ← Protocol B). When Protocol A is exploited, trigger an alert for all
    downstream Protocol B deployments. PATH B (bytecode-diff post-upstream
    disclosure): after a major upstream exploit, audit downstream forks for
    the same vulnerability class and publish a patched/unpatched assessment.
    PATH C (on-chain patch-propagation lag): monitor downstream fork
    deployments for upstream-patched vulnerabilities still present in the
    downstream's deployed bytecode.
  pseudocode: |
    # PATH A — fork-relationship trigger on upstream exploit
    fork_graph ← load_fork_relationships()  # Protocol A ← {Protocol B, ...}

    on upstream_exploit at protocol A with vulnerability V:
      for each downstream protocol B in fork_graph.downstream_of(A):
        patch_status ← check_patch_status(B, V, A.patch_commit)
        exposure_days ← now() − A.exploit_timestamp
        emit(PATH_A, upstream=A, downstream=B, vulnerability=V,
             patch_status, exposure_days,
             severity = critical if patch_status == "unpatched" else low)

    # PATH B — bytecode-diff post-upstream-disclosure
    on upstream_disclosure D for protocol A:
      for each known_fork B of A:
        diff ← compare_bytecode(B.deployed, A.patched, vulnerability=D.vuln_class)
        if diff.vulnerability_present:
          emit(PATH_B, upstream=A, fork=B, vuln_class=D.vuln_class,
               patch_gap=diff.unpatched_functions, severity=critical)

    # PATH C — on-chain patch-propagation lag monitor
    for each upstream_patch P applied at block b_u on protocol A:
      for each downstream fork B of A:
        # Check if B's deployment still has the vulnerable code pattern
        if B.last_upgrade_block < b_u:
          lag ← now() − b_u.timestamp
          if lag > patch_propagation_deadline:
            vuln_still_present ← static_check(B.deployed_bytecode, P.vuln_signature)
            if vuln_still_present:
              emit(PATH_C, fork=B, upstream=A, vulnerability=P.vuln_class,
                   lag_days=lag, severity=critical)

parameters:
  fork_graph:                        { type: object,  default: {} }
  patch_propagation_deadline:        { type: duration, default: 48h }

output_alert: [oak_technique, detection_path, severity, chain,
               upstream_protocol, downstream_fork, vulnerability_class,
               patch_status, exposure_days, evidence]

test_fixtures:
  positive:
    - 2023-hundred-finance-compound-v2-fork-cascade                  # Canonical entry point
    - 2024-sonne-finance-14-month-exposure                           # 14-month unpatched window after upstream disclosure
    - 2024-onyx-protocol-year-plus-exposure                          # >1 year exposure window
    - 2024-curio-makerdao-fork-chain                                 # MakerDAO CDP fork
  negative:
    - "Fork protocol that applied upstream patch within 48 hours of disclosure — monitored, compliant, should not alert"
    - "Protocol with shared architectural pattern but independently developed (not a fork) — no fork relationship in graph"

false_positive_modes:
  - Bytecode similarity from shared libraries (OpenZeppelin, Solmate) rather than a fork relationship — distinguish by full-architecture similarity score vs. library-level similarity
  - Upstream vulnerability class not present in downstream fork (different module, different chain, different asset configuration) — require vulnerability-class confirmation before escalation
  - Fork protocol deployed on a chain where the upstream vulnerability depends on chain-specific preconditions (block time, gas model) not present on the fork's chain
  - Patch applied off-chain (parameter change, emergency pause) not detectable via bytecode diff — supplement with on-chain state comparison

mitigations: [OAK-M09]

reference_implementations:
  - { target: defillama-forks,        chain: cross-chain, url: "" }
  - { target: dedaub-contract-library, chain: cross-chain, url: "" }
  - { target: blocksec-phalcon,       chain: evm,    url: "" }
  - { target: rekt-news,              chain: cross-chain, url: "" }
