diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..0c04fa9 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,52 @@ +FROM mcr.microsoft.com/devcontainers/base:ubuntu + +# Set environment variables +ENV DEBIAN_FRONTEND=noninteractive +ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 +ENV GHIDRA_INSTALL_DIR=/opt/ghidra +ENV GRADLE_VERSION=9.0 +ENV PATH=$PATH:/opt/ghidra:/opt/gradle/bin + +# Install basic dependencies including build tools for Ghidra +RUN apt-get update && apt-get install -y \ + curl \ + wget \ + unzip \ + git \ + build-essential \ + python3 \ + python3-pip \ + python3-venv \ + openjdk-21-jdk \ + bison \ + flex \ + && rm -rf /var/lib/apt/lists/* + +# Install Gradle +RUN wget -q https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip -O /tmp/gradle.zip && \ + unzip -q /tmp/gradle.zip -d /opt && \ + mv /opt/gradle-${GRADLE_VERSION} /opt/gradle && \ + rm /tmp/gradle.zip + +# Create ghidra directories +RUN mkdir -p /opt/ghidra /opt/ghidra-src + +# Create a Python virtual environment and install MCP SDK +RUN python3 -m venv /opt/venv && \ + . /opt/venv/bin/activate && \ + pip install --upgrade pip setuptools wheel && \ + pip install mcp pytest requests + +# Set up vscode user permissions +RUN chown -R vscode:vscode /opt/ghidra /opt/ghidra-src /opt/gradle /opt/venv + +# Switch to vscode user +USER vscode + +# Add virtual environment to PATH for vscode user +ENV PATH="/opt/venv/bin:${PATH}" + +# Verify installations +RUN java -version && gradle --version && python3 --version + +WORKDIR /workspaces/reverse-engineering-assistant \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..bf8ede4 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,51 @@ +{ + "name": "ReVa Development Environment", + "dockerFile": "Dockerfile", + "runArgs": [ + "--init" + ], + "mounts": [ + "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" + ], + "forwardPorts": [8080], + "portsAttributes": { + "8080": { + "label": "ReVa MCP Server", + "onAutoForward": "notify" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "vscjava.vscode-java-pack", + "redhat.java", + "vscjava.vscode-gradle", + "ms-python.python", + "ms-python.pylint", + "ms-python.black-formatter", + "ms-vscode.hexeditor", + "charliermarsh.ruff", + "github.copilot" + ], + "settings": { + "java.jdt.ls.java.home": "/usr/lib/jvm/java-21-openjdk-amd64", + "java.configuration.runtimes": [ + { + "name": "JavaSE-21", + "path": "/usr/lib/jvm/java-21-openjdk-amd64" + } + ], + "java.compile.nullAnalysis.mode": "automatic", + "java.gradle.java.home": "/usr/lib/jvm/java-21-openjdk-amd64" + } + } + }, + "containerEnv": { + "GHIDRA_INSTALL_DIR": "/opt/ghidra", + "GHIDRA_SRC_DIR": "/opt/ghidra-src", + "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-amd64", + "PATH": "${containerEnv:PATH}:/opt/ghidra:/opt/gradle/bin" + }, + "postCreateCommand": ".devcontainer/postCreateCommand.sh", + "remoteUser": "vscode" +} \ No newline at end of file diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh new file mode 100755 index 0000000..55b5dc0 --- /dev/null +++ b/.devcontainer/postCreateCommand.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +# Post-create command script for ReVa devcontainer +set -e + +echo "๐Ÿš€ Setting up ReVa development environment..." + +# Ensure we're in the correct directory +cd /workspaces/reverse-engineering-assistant + +# Verify installations +echo "๐Ÿ“‹ Verifying installations..." +echo "Java version:" +java -version +echo "Gradle version:" +gradle --version +echo "Python version:" +python3 --version + +# Clone and build Ghidra from source if not already done +if [ ! -d "/opt/ghidra-src/.git" ]; then + echo "๐Ÿ“ฆ Cloning Ghidra source code..." + git clone https://github.com/NationalSecurityAgency/ghidra.git /opt/ghidra-src +fi + +if [ ! -f "/opt/ghidra/ghidraRun" ]; then + echo "๐Ÿ”จ Building Ghidra from source (this may take a while)..." + cd /opt/ghidra-src + gradle --no-daemon -I gradle/support/fetchDependencies.gradle init + gradle --no-daemon buildGhidra + + echo "๐Ÿ“ฆ Extracting Ghidra build..." + unzip -q build/dist/ghidra_*_DEV_*.zip -d /opt + mv /opt/ghidra_*_DEV* /opt/ghidra + chmod +x /opt/ghidra/ghidraRun + + cd /workspaces/reverse-engineering-assistant + echo "โœ… Ghidra build complete!" +else + echo "โœ… Ghidra is already built and available" +fi + +# Check if GHIDRA_INSTALL_DIR is properly set +if [ -z "$GHIDRA_INSTALL_DIR" ]; then + echo "โŒ GHIDRA_INSTALL_DIR is not set!" + exit 1 +fi + +if [ ! -d "$GHIDRA_INSTALL_DIR" ]; then + echo "โŒ Ghidra directory does not exist at $GHIDRA_INSTALL_DIR" + exit 1 +fi + +echo "โœ… GHIDRA_INSTALL_DIR is properly set to: $GHIDRA_INSTALL_DIR" +echo "๐Ÿ“‚ Ghidra source is available at: /opt/ghidra-src" + +# Activate Python virtual environment +echo "๐Ÿ Activating Python virtual environment..." +source /opt/venv/bin/activate + +# Install any additional Python dependencies if requirements files exist +if [ -f "cli/requirements.txt" ]; then + echo "๐Ÿ“ฆ Installing Python dependencies from cli/requirements.txt..." + pip install -r cli/requirements.txt +fi + +if [ -f "requirements.txt" ]; then + echo "๐Ÿ“ฆ Installing Python dependencies from requirements.txt..." + pip install -r requirements.txt +fi + +# Verify MCP SDK is installed +echo "๐Ÿ”ง Verifying MCP SDK installation..." +python3 -c "import mcp; print('โœ… MCP SDK is installed')" || echo "โŒ MCP SDK installation failed" + +# Build the project +echo "๐Ÿ”จ Building ReVa extension..." +if gradle clean build; then + echo "โœ… Build successful!" +else + echo "โŒ Build failed!" + exit 1 +fi + +# Run tests to verify everything is working +echo "๐Ÿงช Running unit tests..." +if gradle test --info; then + echo "โœ… Unit tests passed!" +else + echo "โš ๏ธ Unit tests failed - this might be expected in some environments" +fi + +# Create Extensions directory if it doesn't exist +mkdir -p "$GHIDRA_INSTALL_DIR/Ghidra/Extensions" + +echo "๐ŸŽ‰ Setup complete! You can now:" +echo " โ€ข Build the extension with: gradle" +echo " โ€ข Install the extension with: gradle install" +echo " โ€ข Run tests with: gradle test" +echo " โ€ข Run integration tests with: gradle integrationTest --info" +echo " โ€ข Start developing ReVa!" \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 3538722..a25afd5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -ReVa (Reverse Engineering Assistant) is a Ghidra extension that provides a Model Context Protocol (MCP) server for AI-assisted reverse engineering. It uses a streamable transport (SSE) and implements various tools for interacting with Ghidra's capabilities. +ReVa (Reverse Engineering Assistant) is a Ghidra extension that provides a Model Context Protocol (MCP) server for AI-assisted reverse engineering. It uses a streamable transport and implements various tools for interacting with Ghidra's capabilities. ReVa is designed to handle large binaries and entire firmware images efficiently by using smaller, targeted tools that reduce context usage and hallucination. ## Build and Test Commands @@ -107,16 +107,16 @@ When adding new tools to DecompilerToolProvider: ## MCP Server Configuration -The server uses streamable transport (SSE) on port 8080 by default. Configuration is managed through: +The server uses streamable transport on port 8080 by default. Configuration is managed through: - `ConfigManager` - Handles server configuration - `McpServerManager` - Manages the MCP server lifecycle -- Transport: HttpServletStreamableServerTransportProvider (streamable transport) +- Transport: HttpServletStreamableServerTransportProvider (streamable transport, not SSE) ## External Dependencies - Ghidra source code location: `../ghidra` -- MCP SDK: io.modelcontextprotocol.sdk v0.11.1 (uses MCP BOM) -- Jackson: 2.17.0 (forced version for compatibility) -- Jetty: 11.0.25 (embedded servlet support) +- MCP SDK: io.modelcontextprotocol.sdk v0.11.2 (uses MCP BOM) +- Jackson: 2.19.2 (forced version for compatibility) +- Jetty: 11.0.26 (embedded servlet support) - Target: Java 21, Ghidra 11.3+ ## Program Identification @@ -139,11 +139,20 @@ The server uses streamable transport (SSE) on port 8080 by default. Configuratio - **Java**: Target Java 21, minimum Ghidra 11.3+ - **Testing**: Integration tests require `java.awt.headless=false` (GUI environment) - **Build**: Use `gradle` directly, not gradle wrapper -- **MCP SDK**: v0.11.1 with forced Jackson 2.17.0 for compatibility +- **MCP SDK**: v0.11.2 with forced Jackson 2.19.2 for compatibility ## Important Notes - Don't revert to SSE transport (already using streamable) - Fork every integration test to prevent configuration conflicts - **Memory Management**: Always dispose DecompInterface instances to prevent leaks - **Read-Before-Modify**: Decompiler tools enforce function reading before modification -- **Error Messages**: Provide specific, actionable error messages with suggestions \ No newline at end of file +- **Error Messages**: Provide specific, actionable error messages with suggestions + +## Installation Commands +After building, install the extension in Ghidra: +```bash +# Install directly to Ghidra extensions directory +gradle install +``` + +Or install manually via Ghidra's extension manager using the zip file in `dist/`. \ No newline at end of file diff --git a/README.md b/README.md index 4ca668a..940fe2e 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,16 @@ export GHIDRA_INSTALL_DIR=/path/to/ghidra gradle ``` -Then install the extension (in `dist/`) using the Ghidra extension manager. You can also extract the release zip to -the Ghidra extensions directory globally at `${GHIDRA_INSTALL_DIR}/Ghidra/Extensions`. +### Installation Options + +**Option 1: Automatic Installation (Recommended)** +```bash +# Build and install directly to Ghidra +gradle install +``` + +**Option 2: Manual Installation** +Install the extension (in `dist/`) using the Ghidra extension manager, or extract the release zip to the Ghidra extensions directory at `${GHIDRA_INSTALL_DIR}/Ghidra/Extensions`. After installing the extension you need to activate it in two places: diff --git a/src/main/java/reva/server/CLAUDE.md b/src/main/java/reva/server/CLAUDE.md index ab3404d..70df09f 100644 --- a/src/main/java/reva/server/CLAUDE.md +++ b/src/main/java/reva/server/CLAUDE.md @@ -8,8 +8,8 @@ The `reva.server` package contains the core MCP (Model Context Protocol) server ### Key Architecture Components -- **MCP Server**: Built on MCP SDK v0.11.0 with streamable transport -- **HTTP Server**: Jetty 11.0.25 embedded servlet container +- **MCP Server**: Built on MCP SDK v0.11.2 with streamable transport +- **HTTP Server**: Jetty 11.0.26 embedded servlet container - **Transport Layer**: HttpServletStreamableServerTransportProvider for real-time streaming - **Service Registry**: Integration with RevaInternalServiceRegistry for component coordination - **Configuration Management**: Dynamic configuration with hot-reload capabilities diff --git a/src/test.slow/java/reva/CLAUDE.md b/src/test.slow/java/reva/CLAUDE.md index e5402c9..a96b85e 100644 --- a/src/test.slow/java/reva/CLAUDE.md +++ b/src/test.slow/java/reva/CLAUDE.md @@ -1,3 +1,28 @@ -- When writing integration tests, make sure to set up the program, call the tool and then validate the output. If the tool modifies the program, validate the modification. -- Don't write useless tests, make sure they have a purpose. -- **CRITICAL**: Integration tests should validate actual Ghidra program state changes, not just MCP tool responses +# Integration Test Development Guidelines + +## Test Design Philosophy + +- **State Validation**: Always validate actual Ghidra program state changes, not just MCP tool responses +- **Purpose-Driven Testing**: Only write tests that verify meaningful functionality +- **End-to-End Validation**: Set up program state, execute tools, validate both response and program modifications + +## Critical Testing Requirements + +1. **Program State Validation**: + - Use `Function.getParameters()` and `Function.getAllVariables()` to validate variable changes + - Use `DataType.isEquivalent()` to compare datatypes before/after changes + - Check actual symbol table entries, not just tool responses + +2. **Shared Test Environment**: + - Tests use shared Ghidra environment for faster execution + - Each test gets a fresh program via `createDefaultProgram()` + - MCP server persists across tests within the same class + +3. **Test Isolation**: + - Fork every test to prevent configuration conflicts + - Programs are automatically registered/unregistered with MCP server + - Always wrap program modifications in transactions + +## Test Requirements +- Tests run with `java.awt.headless=false` (GUI environment required) +- **You are not finished until all tests pass!**