Karya Semi
HomeBlogSearchCategoriesAboutContact
Karya Semi

Less noise. More notes.

HomeBlogAboutContactPrivacy PolicyDisclaimer

© 2026 Karya Semi. All rights reserved.

XGitHubLinkedIn
  1. Home
  2. /Categories
  3. /AI

Transforming LLM Context Memory into Program Analysis Engines

Use llm memory program analysis to scan code. Run static evaluation and detect vulnerabilities in long context. Automate security checks now.

Dian Rijal Asyrof/August 31, 2026/6 min read
Illustration for Transforming LLM Context Memory into Program Analysis Engines

Static analysis has always been a game of strict rules. Traditional tools generate an Abstract Syntax Tree (AST), write patterns against that tree, and flag matches. If a developer uses a dangerous function like eval() or runs an unparameterized SQL query, the scanner flags it. This works well for simple, localized patterns.

But code does not live in isolation. Vulnerabilities usually span multiple files, dynamic class loaders, and framework lifecycles. Traditional static application security testing (SAST) tools struggle here. They fail when data flows through dynamic imports, dependency injection containers, or complex helper functions. Building a complete, accurate call graph for a dynamic language like Python or JavaScript is incredibly difficult.

The arrival of massive context windows changes this equation. While managing context window costs is a challenge, when a model can accept two million tokens in a single prompt, you no longer need to chunk your codebase into tiny, isolated pieces. You can load the entire source tree directly into the model's memory. This turns the model from a simple autocomplete assistant into a simulated execution environment. It can trace data flow across boundaries that break standard compilers and static analyzers.

Why ASTs Fall Short

To understand why we need LLM context memory for program analysis, look at how traditional tools analyze code. An AST parser reads source code and converts it into a hierarchical tree structure. A variable assignment is a node; a function call is another node.

To trace a vulnerability, a scanner must perform taint analysis. It identifies a source (where user input enters the system, like an HTTP request parameter) and a sink (where that input is executed, like a database query or system command). The scanner then traverses the AST to see if any path connects the source to the sink without passing through a sanitization function.

This approach breaks down in real-world applications. Consider this Python example:

# app/utils.py
def get_helper(strategy_name):
    module = __import__("app.helpers")
    return getattr(module, strategy_name)
 
# app/main.py
@app.route("/execute")
def execute():
    action = request.args.get("action")
    helper = get_helper(action)
    return helper()

An AST parser cannot easily resolve what helper() executes at runtime. The import is dynamic, and the attribute is retrieved using a string variable. To a traditional scanner, the path goes cold. The tool either flags every dynamic import as a generic warning or misses the path entirely.

An LLM reads this code differently. It does not construct a formal mathematical graph. Instead, its attention mechanism associates the string action with the parameter strategy_name, tracks it through the dynamic import, and recognizes that an attacker can control which helper class is instantiated. The model uses its training on language patterns to infer the execution flow, bridging the gap that static parsers cannot cross.

Structuring the Codebase Payload

You cannot simply dump thousands of raw source files into a prompt and expect the model to find bugs. Raw, unstructured text causes attention decay. The model loses track of file boundaries and confuses helper functions with main entry points.

To build a reliable analysis engine, you must structure the input payload systematically. Start with a directory tree map. This gives the model a geographic layout of the codebase before it reads the actual files.

Format the files using clear, structured XML tags. XML tags are easier for LLMs to parse than markdown code blocks because they provide explicit, nested boundaries. Include the relative path as an attribute in the opening tag.

<repository>
<directory_structure>
- src/
  - app.py
  - database.py
  - auth.py
</directory_structure>
 
<file path="src/app.py">
import database
from flask import Flask, request
app = Flask(__name__)
 
@app.route("/user")
def get_user():
    user_id = request.args.get("id")
    return database.query_user(user_id)
</file>
 
<file path="src/database.py">
import sqlite3
 
def query_user(user_id):
    conn = sqlite3.connect("users.db")
    cursor = conn.cursor()
    # Vulnerable query construction
    query = f"SELECT * FROM users WHERE id = '{user_id}'"
    cursor.execute(query)
    return cursor.fetchone()
</file>
</repository>

Keep the formatting clean. Strip unnecessary assets, compiled binaries, and lock files. Keep comments and docstrings, as they contain valuable semantic information about developer intent. Do not minify the source code. Minification destroys the tokenization structure, making the code look like an endless string of random characters, which degrades the model's reasoning capabilities. This is similar to the degradation seen in local LLM execution when parameters are not optimized.

Tracing Data Flow with Chain-of-Thought

Once the codebase is loaded into memory, you must instruct the model to perform the analysis. If you simply ask, "Are there any SQL injection bugs here?", the model will guess. It might find obvious ones, but it will miss complex paths and generate false positives.

You must force the model to perform explicit taint analysis using a structured chain of thought. Instruct the model to write down its step-by-step reasoning before outputting its final assessment. Keep in mind that exposing these steps can introduce risks like stealing reasoning traces from API responses.

Here is a system prompt structure that forces this behavior:

You are a static program analysis engine. Analyze the provided codebase payload.
Your goal is to trace untrusted user inputs (sources) to dangerous execution points (sinks).
 
For every potential vulnerability, you must output a structured trace:
1. Source: The exact file, line number, and variable where user input enters.
2. Sink: The exact file, line number, and function where the variable is executed.
3. Path: A step-by-step list of every function call, assignment, and file transition that the variable undergoes.
4. Sanitization Check: Identify if the data passes through any validation or escaping functions.
5. Verdict: State whether the path is vulnerable or safe, explaining why.
 
Write out the execution path step-by-step. Do not skip steps.

By forcing the model to write down the path, you anchor its attention. If the model must output the variable assignment at each step, it is much less likely to hallucinate a connection that does not exist.

Mitigating Attention Decay

Even the best long-context models suffer from performance degradation when processing millions of tokens. This is the "lost in the middle" problem. Information placed near the beginning or the end of the context window is recalled with high accuracy, while information in the middle is frequently missed.

If you load a large repository and place a vulnerable utility function in the exact middle of the payload, the model might overlook it.

To mitigate this, run a two-pass analysis pipeline.

[Raw Codebase] 
      │
      ▼
[Pass 1: Indexer Prompt] ──> Extracts potential sources & sinks
      │
      ▼
[Pass 2: Analyzer Prompt] ──> Traces paths between indexed points

In the first pass, ask the model to generate a simple index of all entry points (API routes, CLI arguments, file readers) and all potential sinks (database queries, shell executions, file writers). This is a low-reasoning task that requires the model to scan for specific keywords and patterns.

In the second pass, feed the generated index back into the model along with the codebase. Instruct the model to trace the paths specifically between the indexed sources and sinks. Because the model now has explicit targets to look for, its attention is directed to the correct locations in the context window, bypassing the decay issue.

A Practical Python Implementation

Here is a complete, runable script that packages a local directory into the XML format required for long-context analysis. It reads the files, filters out ignored directories, and prepares the payload.

import os
from pathlib import Path
 
def build_payload(root_dir, ignore_dirs=None, extensions=None):
    if ignore_dirs is None:
        ignore_dirs = {".git", "__pycache__", "node_modules", "venv", "env"}
    if extensions is None:
        extensions = {".py", ".js", ".ts", ".go", ".java", ".json"}
 
    root = Path(root_dir)
    payload = []
    
    # Generate tree structure representation
    tree_lines = []
    for path in sorted(root.rglob("*")):
        if any(part in ignore_dirs for part in path.parts):
            continue
        depth = len(path.relative_to(root).parts) - 1
        indent = "  " * depth
        tree_lines.append(f"{indent}- {path.name}")
    
    payload.append("<repository>")
    payload.append("<directory_structure>")
    payload.extend(tree_lines)
    payload.append("</directory_structure>\n")
    
    # Append file contents
    for path in sorted(root.rglob("*")):
        if not path.is_file():
            continue
        if any(part in ignore_dirs for part in path.parts):
            continue
        if path.suffix not in extensions:
            continue
            
        try:
            content = path.read_text(encoding="utf-8", errors="ignore")
            rel_path = path.relative_to(root)
            payload.append(f'<file path="{rel_path}">')
            payload.append(content)
            payload.append("</file>\n")
        except Exception as e:
            # Skip unreadable files silently
            continue
            
    payload.append("</repository>")
    return "\n".join(payload)
 
# Example usage:
# repo_payload = build_payload("./my-project")
# print(repo_payload[:500])

This script converts a directory tree into a single structured string. You can append your system prompt to this string and send it to your chosen long-context LLM API.

Cost, Latency, and the Hybrid Future

Using LLMs as program analysis engines is not cheap. Sending a million tokens to an API costs money and takes time. While some providers run smaller and faster models at scale to optimize costs, a single analysis run can take several minutes to complete, which is too slow for a standard commit hook or a fast CI/CD pipeline.

Traditional static analysis tools run in seconds and cost next to nothing. They should remain your first line of defense.

The most efficient setup is a hybrid pipeline. Use traditional AST-based scanners to run quick checks on every commit. If the scanner flags a potential issue but cannot confirm it due to path complexity, trigger the LLM engine.

Alternatively, use the LLM engine during nightly builds or security audit phases. The model can analyze the entire codebase to find logic flaws, authentication bypasses, and complex injection paths that traditional tools miss entirely.

And because the model understands natural language, you can ask it to generate a remediation patch for the specific vulnerability it found. Instead of just flagging a line of code, the engine output includes a rewritten version of the file that fixes the bug without breaking surrounding functionality.

DR

Dian Rijal Asyrof

Writes about useful AI tools, programming practice, and the craft of building reliable software.

Previous articleBuilding Local-First Password Management Architecture with SesameNext articlePorting Gemma 4 in Pure JAX Across TPU and GPU Architectures
LLMLLMsContextMemoryStatic Analysis
On this page↓
  1. Why ASTs Fall Short
  2. Structuring the Codebase Payload
  3. Tracing Data Flow with Chain-of-Thought
  4. Mitigating Attention Decay
  5. A Practical Python Implementation
  6. Cost, Latency, and the Hybrid Future

On this page

  1. Why ASTs Fall Short
  2. Structuring the Codebase Payload
  3. Tracing Data Flow with Chain-of-Thought
  4. Mitigating Attention Decay
  5. A Practical Python Implementation
  6. Cost, Latency, and the Hybrid Future

See also

Illustration for Open Source Experiential Router Uses Request Data to Fine-Tune Models
Programming/Aug 28, 2026

Open Source Experiential Router Uses Request Data to Fine-Tune Models

New API router uses request data for openrouter model fine tuning. Turn inference routing patterns into training feedback for better LLMs.

6 min read
LLMLLMs
Illustration for Why Local LLM Execution Yields Subpar Reasoning Output
AI/Aug 28, 2026

Why Local LLM Execution Yields Subpar Reasoning Output

Aggressive quantization, small context windows, bad samplers explain why local llm dumber. Adjust parameters to restore reasoning.

6 min read
LLMLLMs
Illustration for Nvidia Research Shows Agent Harness Matters More Than Underlying AI Model
AI/Aug 28, 2026

Nvidia Research Shows Agent Harness Matters More Than Underlying AI Model

Fine-tuning nvidia ai agent harness stops execution drift. New research proves system design beats raw model power for complex task completion.

7 min read
AI AgentsNvidia