rules:
    - id: balancer-readonly-reentrancy-getpooltokens
      message: $VAULT.getPoolTokens() call on a Balancer pool is not protected from the read-only reentrancy.
      metadata:
        category: security
        technology:
            - solidity
        cwe: "CWE-841: Improper Enforcement of Behavioral Workflow"
        confidence: HIGH
        likelihood: MEDIUM
        impact: HIGH
        subcategory:
            - vuln
        references: 
          - https://quillaudits.medium.com/decoding-sentiment-protocols-1-million-exploit-quillaudits-f36bee77d376
          - https://hackmd.io/@sentimentxyz/SJCySo1z2
      patterns:
        - pattern-either:
          - pattern: |
              function $F(...) {
                ...
                $RETURN = $VAULT.getPoolTokens(...);
                ...
              }
        - metavariable-pattern:
              metavariable: $RETURN
              pattern-regex: .*uint256\[].*
        - pattern-not-inside: |
            contract $C {
              ...
              function $CHECKFUNC(...) {
                ...
                VaultReentrancyLib.ensureNotInVaultContext(...);
                ...
              }
              ...
              function $F(...) {
                ...
                $CHECKFUNC(...);
                ...
                $RETURN = $VAULT.getPoolTokens(...);
                ...
              }
              ...
            }
        - pattern-not-inside: |
            contract $C {
              ...
              function $CHECKFUNC(...) {
                ...
                VaultReentrancyLib.ensureNotInVaultContext(...);
                ...
              }
              ...
              function $F(...) {
                ...
                $RETURN = $VAULT.getPoolTokens(...);
                ...
                $CHECKFUNC(...);
                ...
              }
              ...
            }
        - pattern-not-inside: |
            contract $C {
              ...
              function $CHECKFUNC(...) {
                ...
                $VAULT.manageUserBalance(...);
                ...
              }
              ...
              function $F(...) {
                ...
                $RETURN = $VAULT.getPoolTokens(...);
                ...
                $CHECKFUNC(...);
                ...
              }
              ...
            }
        - pattern-not-inside: |
            contract $C {
              ...
              function $CHECKFUNC(...) {
                ...
                $VAULT.manageUserBalance(...);
                ...
              }
              ...
              function $F(...) {
                ...
                $CHECKFUNC(...);
                ...
                $RETURN = $VAULT.getPoolTokens(...);
                ...
              }
              ...
            }
        - pattern-not: |
            function $F(...) {
              ...
              VaultReentrancyLib.ensureNotInVaultContext(...);
              ...
            }
        - pattern-not: |
            function $F(...) {
              ...
              $VAULT.manageUserBalance(...);
              ...
            }
        - pattern-not-inside: |
            contract LinearPool {
              ...
            }
        - pattern-not-inside: |
            contract ComposableStablePool {
              ...
            }
        - pattern-not-inside: |
            contract BalancerQueries {
              ...
            } 
        - pattern-not-inside: |
            contract ManagedPool {
              ...
            }
        - pattern-not-inside: |
            contract BaseWeightedPool {
              ...
            } 
        - pattern-not-inside: |
            contract ComposableStablePoolStorage {
              ...
            }
        - pattern-not-inside: |
            contract RecoveryModeHelper {
              ...
            }
        - focus-metavariable:
            - $VAULT
      languages:
        - solidity
      severity: ERROR