Análise Estática de Código com AST
Vitor Lobo

Vitor Lobo @scovl

About: A simple guy who enjoys a minimalist life.

Location:
Brazil
Joined:
Sep 15, 2019

Análise Estática de Código com AST

Publish Date: Feb 4
1 1

Você já se perguntou como ferramentas de análise estática, como o Semgrep, conseguem identificar padrões e potenciais problemas no código sem executá-lo? A resposta está no modelo de Árvore Sintática Abstrata (AST), uma representação estruturada e hierárquica do código-fonte.

Nessa abordagem em vez de tratar o código apenas como uma sequência de caracteres, a AST organiza os elementos do código (como funções, variáveis, expressões, etc.) em nós interconectados, facilitando a análise e a manipulação.

Essa abordagem é fundamental para ferramentas de análise estática, que percorrem a AST em busca de padrões específicos, vulnerabilidades e boas práticas sem a necessidade de executar o código.

Exemplo Prático com Python

Vamos utilizar o módulo nativo ast do Python para converter um exemplo simples de código em sua AST e, em seguida, analisar essa estrutura. Considere o seguinte código em Python:

def soma(a, b):
    return a + b

resultado = soma(2, 3)
Enter fullscreen mode Exit fullscreen mode

Esse código define:

  • Uma função soma(a, b) que retorna a soma dos parâmetros.
  • Uma chamada à função soma que atribui o resultado à variável resultado.

Gerando a AST

Utilizando o módulo ast, podemos transformar o código acima em uma árvore sintática:

import ast

codigo = """
def soma(a, b):
    return a + b

resultado = soma(2, 3)
"""

# Parseia o código e gera a AST
arvore_ast = ast.parse(codigo)

# Exibe a AST de forma legível
print(ast.dump(arvore_ast, indent=4))
Enter fullscreen mode Exit fullscreen mode

Ao executar esse script, você obterá uma saída semelhante a:

Module(
    body=[
        FunctionDef(
            name='soma',
            args=arguments(
                posonlyargs=[],
                args=[
                    arg(arg='a'),
                    arg(arg='b')
                ],
                vararg=None,
                kwonlyargs=[],
                kw_defaults=[],
                kwarg=None,
                defaults=[]
            ),
            body=[
                Return(
                    value=BinOp(
                        left=Name(id='a', ctx=Load()),
                        op=Add(),
                        right=Name(id='b', ctx=Load())
                    )
                )
            ],
            decorator_list=[],
            returns=None,
            type_comment=None
        ),
        Assign(
            targets=[
                Name(id='resultado', ctx=Store())
            ],
            value=Call(
                func=Name(id='soma', ctx=Load()),
                args=[
                    Constant(value=2),
                    Constant(value=3)
                ],
                keywords=[]
            ),
            type_comment=None
        )
    ],
    type_ignores=[]
)
Enter fullscreen mode Exit fullscreen mode

Analisando a saída:

  • Module: Representa o módulo inteiro, ou seja, o arquivo de código.
  • FunctionDef: Define a função soma com seus argumentos a e b.
  • Return: Representa a instrução de retorno dentro da função, que retorna o resultado da operação a + b.
  • BinOp: Representa a operação binária, no caso, a soma (Add) entre a e b.
  • Assign: Representa a atribuição da chamada da função soma(2, 3) à variável resultado.

Ferramentas de análise estática, como o Semgrep, percorrem a AST para identificar padrões específicos. Vamos ver um exemplo de como podemos encontrar operações de soma utilizando um visitor customizado abaixo:

class EncontrarSomas(ast.NodeVisitor):
    def visit_BinOp(self, node):
        if isinstance(node.op, ast.Add):  # Verifica se a operação é uma soma
            print(f"Encontrado: Operação de soma na linha {node.lineno}")
        # Continua a visita a outros nós
        self.generic_visit(node)

# Executa o analisador na AST gerada
analisador = EncontrarSomas()
analisador.visit(arvore_ast)
Enter fullscreen mode Exit fullscreen mode

Ao executar este código, a saída será:

Encontrado: Operação de soma na linha 3
Enter fullscreen mode Exit fullscreen mode

Isso indica que o analisador encontrou uma operação de soma na linha em que o return a + b foi definido.

A transformação do código-fonte em uma Árvore Sintática Abstrata (AST) é uma abordagem interessante que se explorada a parte estática com a implementação dinâmica e recursos de IA, tem um potencial enorme em soluções de qualidade de código.

Com esse conhecimento, você pode começar a explorar e desenvolver suas próprias ferramentas de análise estática, seja para detectar vulnerabilidades, impor padrões de código ou otimizar seu fluxo de trabalho de desenvolvimento.

Comments 1 total

  • Henrique Lobo Weissmann (Kico)
    Henrique Lobo Weissmann (Kico)Feb 4, 2025

    Quem tem recursos MUITO poderosos com AST? Groovy

    Dá pra fazer coisas cabulosas nisto (groovy-lang.org/metaprogramming.html). E aí tá o que é lindo e horroroso ao mesmo tempo.

    É lindo o poder que te dá. Mas como muitas coisas podem parecer mágica pra outros usuários do código, pode ser um pesadelo também.

    Fundamental treinar a equipe.

Add comment