DEV Community

Vitor Lobo
Vitor Lobo

Posted on

1

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

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.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (1)

Collapse
 
loboweissmann profile image
Henrique Lobo Weissmann (Kico)

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.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more