diff --git a/.github/workflows/rigging_pr_description.yml b/.github/workflows/rigging_pr_description.yml index 5bb8b8f..c936345 100644 --- a/.github/workflows/rigging_pr_description.yml +++ b/.github/workflows/rigging_pr_description.yml @@ -21,11 +21,10 @@ jobs: id: diff # shellcheck disable=SC2102 run: | - git fetch origin "${{ github.base_ref }}" - MERGE_BASE=$(git merge-base HEAD "origin/${{ github.base_ref }}") - # Use separate diff arguments instead of range notation - DIFF=$(git diff "$MERGE_BASE" HEAD | base64 --wrap=0) - echo "diff=${DIFF}" >> "$GITHUB_OUTPUT" + git fetch origin "${GITHUB_BASE_REF}" + MERGE_BASE="$(git merge-base HEAD "origin/${GITHUB_BASE_REF}")" + DIFF="$(git diff "${MERGE_BASE}" HEAD | base64 --wrap=0)" + echo "diff=${DIFF}" >> "${GITHUB_OUTPUT}" - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b #v5.0.3 with: python-version: "3.11" @@ -46,6 +45,14 @@ jobs: GIT_DIFF: ${{ steps.diff.outputs.diff }} run: | python .github/scripts/rigging_pr_decorator.py + # Extract PR body + - name: Extract PR body + id: pr + run: | + PR_BODY="$(gh pr view "${GITHUB_EVENT_PULL_REQUEST_NUMBER}" --json body --jq .body)" + echo "body=${PR_BODY}" >> "${GITHUB_OUTPUT}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Update the PR description - name: Update PR Description uses: nefrob/pr-description@4dcc9f3ad5ec06b2a197c5f8f93db5e69d2fdca7 #v1.2.0 diff --git a/.gitignore b/.gitignore index a0dd9e8..aedf101 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,70 @@ -.DS_Store +### Project Specific ### +# burpference logs and local config logs/ -.idea/workspace.xml +.burpference/ +configs/* +!configs/*.example.json +prompt.txt + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +*.so + +# Distribution / packaging +dist/ +build/ +*.egg-info/ +*.egg + +# Virtual environments +venv/ +env/ +.env/ +.venv/ + +# Testing +.pytest_cache/ +.coverage +coverage.xml +htmlcov/ + +# IDE specific files +.idea/ .vscode/ -.env -archive/autogpt/.gradle/* -archive/autogpt/.gradle/buildOutputCleanup/cache.properties -.lock +*.swp +*.swo +*~ +.DS_Store + +# Temporary files +*.log +*.tmp +*.temp -# Ignore Gradle project-specific cache directory -.gradle +# Debug files +*.debug -# Ignore Gradle build output directory -build +# Local development +local_settings.py +*.local.json +*.local.py + +# Security related +*.key +*.pem +*.cert +*.password +*.token +.env +.secret -# Ignore $py.class files (generated when running burp) +# Java/Burp related +*.class +*.jar +!lib/*.jar # Keep vendored jar files if needed -.*$py.*class -burpference/api_adapters$py.class -burpference/consts$py.class +# Docs build +docs/_build/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 54313f5..e2e0634 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,6 +17,7 @@ repos: hooks: - id: actionlint name: Check Github Actions + args: ["--ignore", "SC2102"] # Python code security - repo: https://github.com/PyCQA/bandit diff --git a/README.md b/README.md index 68e2f5b..e8ffba8 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ Some key features: - **Comprehensive Logging**: A logging system allows you to review intercepted responses, API requests sent, and replies received—all clearly displayed for analysis. - A clean table interface displaying all logs, intercepted responses, API calls, and status codes for comprehensive engagement tracking. - Stores inference logs in both the "_Inference Logger_" tab as a live preview and a timestamped file in the /logs directory. +- **Native Burp Reporting**: burpference' system prompt invokes the model to make an assessment based on severity level of the finding which is color-coded (a heatmap related to the severity level) in the extenstion tab. + - Additionally, burpference "findings" are created as issues in the Burp Scanner navigation bar available across all tabs in the Burp UI. - **Flexible Configuration**: Customize system prompts, API keys, or remote hosts as needed. Use your own configuration files for seamless integration with your workflow. - Supports custom configurations, allowing you to load and switch between system prompts, API keys, and remote hosts - [Several examples](configs/README.md) are provided in the repository, and contributions for additional provider plugins are welcome. diff --git a/burpference/burpference.py b/burpference/burpference.py index 94be978..864d4df 100644 --- a/burpference/burpference.py +++ b/burpference/burpference.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # type: ignore[import] -from burp import IBurpExtender, ITab, IHttpListener +from burp import IBurpExtender, ITab, IHttpListener, IScanIssue from java.awt import BorderLayout, GridBagLayout, GridBagConstraints, Font from javax.swing import ( JPanel, JTextArea, JScrollPane, @@ -15,6 +15,7 @@ from datetime import datetime from consts import * from api_adapters import get_api_adapter +from issues import BurpferenceIssue def load_ascii_art(file_path): @@ -144,7 +145,7 @@ def compare(self, s1, s2): n2 = int(s2) return n1 - n2 except: - return 0 # Return 0 if conversion fails + return 0 # Add sorting capability sorter = TableRowSorter(self.historyTableModel) @@ -598,6 +599,57 @@ def applyDarkTheme(self, component): for child in component.getComponents(): self.applyDarkTheme(child) + def map_severity(self, ai_severity): + """Map burpference model severity levels to Burp's iscan exact severity strings""" + severity_map = { + "CRITICAL": "High", + "HIGH": "High", + "MEDIUM": "Medium", + "LOW": "Low", + "INFORMATIONAL": "Information" + } + return severity_map.get(ai_severity, "Information") + + def extract_severity_from_response(self, response): + """Extract severity level from model response""" + for level in ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFORMATIONAL"]: + if "**%s**" % level in response: + return level + return "INFORMATIONAL" + + def create_scan_issue(self, messageInfo, processed_response): + try: + severity = self.extract_severity_from_response(processed_response) + burp_severity = self.map_severity(severity) + + # Convert response to string and handle escaping + if isinstance(processed_response, str): + detail = processed_response + else: + detail = str(processed_response) + + if detail.startswith('"') and detail.endswith('"'): + detail = detail[1:-1] # Remove surrounding quotes + + # Create properly formatted issue name + issue_name = "burpference: %s Security Finding" % severity + + issue = BurpferenceIssue( + httpService=messageInfo.getHttpService(), + url=self._helpers.analyzeRequest(messageInfo).getUrl(), + httpMessages=[messageInfo], + name=issue_name, + detail=detail, + severity=burp_severity, + confidence="Certain" + ) + + self._callbacks.addScanIssue(issue) + self.log_message("Added %s issue to Burp Scanner" % severity) + + except Exception as e: + self.log_message("Error creating scan issue: %s" % str(e)) + def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): if not self.is_running: return @@ -731,6 +783,9 @@ def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): # Only update the main UI if we got a successful response if status == "Success" and self.is_running: + # Create Burp scanner issue + self.create_scan_issue(messageInfo, processed_response) + # Add to history table with metadata self.historyTableModel.addRow([ table_metadata.get("id", ""), diff --git a/burpference/issues.py b/burpference/issues.py new file mode 100644 index 0000000..cdc22be --- /dev/null +++ b/burpference/issues.py @@ -0,0 +1,47 @@ +from burp import IScanIssue + + +class BurpferenceIssue(IScanIssue): + def __init__( + self, httpService, url, httpMessages, name, detail, severity, confidence + ): + self._httpService = httpService + self._url = url + self._httpMessages = httpMessages + self._name = name + self._detail = detail + self._severity = severity + self._confidence = confidence + + def getUrl(self): + return self._url + + def getIssueName(self): + return self._name + + def getIssueType(self): + return 0 # Custom issue type + + def getSeverity(self): + return self._severity + + def getConfidence(self): + return self._confidence + + def getIssueBackground(self): + return "Issue identified by burpference model analysis" + + def getRemediationBackground(self): + return "Verify the models findings and remediate accordingly" + + def getIssueDetail(self): + return self._detail + + def getRemediationDetail(self): + return None + + def getHttpMessages(self): + return self._httpMessages + + def getHttpService(self): + return self._httpService