Home/Docs/Extensions/Extension API Reference

Extensions

Extension API Reference

Complete reference for building custom InterceptSuite Python extensions.

Extension API Reference

Complete reference for developing custom InterceptSuite Python extensions.

Extension Class Requirements

Every extension must define a class named InterceptSuiteExtension. InterceptSuite only loads files that contain this class -it is required.

class InterceptSuiteExtension:
    def register_interceptor_api(self, interceptor):
        interceptor.set_extension_name('My Extension')
        interceptor.set_extension_version('1.0.0')

Required Methods

register_interceptor_api(self, interceptor)

This method is called by InterceptSuite when the extension loads. It must be present in InterceptSuiteExtension.

Parameters:

Parameter Type Description
interceptor object The InterceptSuite API object

Required API Calls

Within register_interceptor_api, you must call both of the following:

interceptor.set_extension_name('My Extension')    # Required
interceptor.set_extension_version('1.0.0')        # Required

Both methods accept a string parameter. Without these calls, the extension will fail to load.

Extension Logging

To write messages to the Extension Logs tab inside InterceptSuite, import and use ExtensionLogger:

from InterceptSuite.Extensions.APIs.Logging import ExtensionLogger

ExtensionLogger.Log("Extension loaded successfully!")

Log messages appear in the extension's own output pane: open the Extension tab, select the extension in the table, and read its output underneath (the Clear button empties it). Each extension has its own output, so two extensions never mix their lines. Output that cannot be tied to an extension, such as Python start-up problems, goes to the application Logs tab. Any string is accepted.

Full Registration Example

from InterceptSuite.Extensions.APIs.Logging import ExtensionLogger

class InterceptSuiteExtension:
    def register_interceptor_api(self, interceptor):
        interceptor.set_extension_name('My Custom Extension')
        interceptor.set_extension_version('1.0.0')
        ExtensionLogger.Log("Extension registered successfully!")

Custom Data Viewer Tabs

Extensions can add tabs to the packet viewer alongside the built-in Raw and Hex tabs. This is done by registering a handler class with AddDataViewerTab.

Registering a Custom Tab

interceptor.AddDataViewerTab("Base64", MyHandlerClass())

Parameters:

Parameter Type Description
Tab Name string The label shown on the tab
Handler Instance object An instance of your handler class

Rules for registering a tab:

  • Call set_extension_name and set_extension_version first. AddDataViewerTab raises an error if either is missing.
  • The tab name must not be empty.
  • One extension can register several tabs, each with its own handler.
  • Tabs are removed automatically when the extension is disabled, removed or the app closes.

Handler Class Requirements

Your handler class has three methods. Only fetchdata is required; the other two are optional:

Method Required If it is missing
fetchdata(data) Yes AddDataViewerTab raises an error, the failure is logged and the extension does not load.
should_show_tab(data) No The tab is shown for every packet.
updatedata(data) No Edits made in the tab are not applied: the packet stays as it was.
class MyHandlerClass:
    def should_show_tab(self, data):
        """Return True to show the tab for this packet, False to hide it."""
        return True

    def fetchdata(self, data):
        """Process packet data and return the content to display in the tab."""
        raw = data.get("data", "")
        return raw  # Return processed string

    def updatedata(self, data):
        """Handle user edits made in the tab. Return updated data or None."""
        edited = data.get("edited_data", "")
        if edited:
            return edited  # Return the updated packet content
        return None

Where Tabs Appear

A registered tab is offered in every place that shows packet data. The tool_type key tells you which one:

tool_type Where Editable
proxy_history Proxy History No
intercept Intercept (held packet) Yes
replay Replay (payload to send, and received packets) Compose only

should_show_tab is honoured in all of them, so a tab appears only when it applies to the packet.

The data Dictionary

The data parameter passed to all handler methods is a dictionary:

{
    "ip":               "23.215.0.136",       # str       - Source IP address
    "destination_ip":   "127.0.0.1",          # str       - Destination IP address
    "source_port":      0,                    # int       - Source port
    "destination_port": 443,                  # int       - Destination (server) port
    "direction":        "Client->Server",     # str       - "Client->Server" or "Server->Client"
    "length":           1452,                 # int       - Packet length in bytes
    "data":             "payload",            # str       - Payload as a Latin-1 string
    "raw_data":         bytearray(b"..."),    # bytearray - Payload bytes
    "type":             "Text",               # str       - Data type
    "timestamp":        "2026-10-04 12:35:26",# str       - Timestamp
    "connection_id":    1,                    # int       - Connection identifier
    "packet_id":        42,                   # int       - Packet identifier
    "host":             "example.com",        # str       - Domain the client used ("" if it connected by IP)
    "alpn":             "h2",                 # str       - Negotiated ALPN ("" if none)
    "protocol":         "TLS",                # str       - TCP, TLS, UDP, DTLS or QUIC
    "tool_type":        "proxy_history",      # str       - proxy_history, intercept or replay
    "is_editable":      False,                # bool      - True where the user can edit
    "editable_data":    "payload",            # str       - The packet's current editable content (Latin-1 string)
    "edited_data":      "..."                 # str       - Only in updatedata(): the tab's edited content
}

Notes on the values:

  • data and edited_data are Latin-1 strings: one character per byte (0 to 255), so binary payloads survive unchanged. Use raw_data when you want real bytes.
  • host is the real server name from the client's TLS SNI, or the domain a SOCKS5 client asked for. It is an empty string when the client connected by IP; InterceptSuite never invents one.
  • alpn is empty when no ALPN was negotiated, for example for plain TCP or UDP.
  • type is the data type label shown in Proxy History, such as Text or Binary.

The direction uses the same wording everywhere. In Replay, what you send is Client->Server and what comes back is Server->Client.

Because protocol, alpn and host are provided, one extension can tell, for example, HTTP/3 over QUIC from plain TCP on the same port, without guessing from the bytes.

Method Reference

should_show_tab(self, data) -> bool

Controls visibility of your custom tab for each packet.

  • Return True to show the tab
  • Return False to hide the tab

Use this to show your tab only when the packet data is relevant:

def should_show_tab(self, data):
    raw = data.get("data", "")
    return "HTTP" in raw  # Only show for HTTP packets

fetchdata(self, data) -> str

Processes the packet data for display in your tab.

  • Receives the full data dictionary
  • Returns a string to display in the tab
def fetchdata(self, data):
    import base64
    raw = data.get("data", "")
    try:
        return base64.b64decode(raw).decode("utf-8", errors="ignore")
    except Exception:
        return "Invalid base64 data"

updatedata(self, data) -> str | None

Handles user edits made in your custom tab.

  • data.get("edited_data") contains what the user typed in your tab
  • Return the updated packet content to replace the original payload
  • Return None if no update should be applied
  • The string you return is turned back into bytes with Latin-1, one character per byte. For binary results build the string with bytes_value.decode("latin-1"); do not return UTF-8 text for binary data
def updatedata(self, data):
    import base64
    edited = data.get("edited_data", "")
    if edited:
        try:
            return base64.b64encode(edited.encode("utf-8")).decode("utf-8")
        except Exception:
            return None
    return None

Errors and Defaults

An exception in your handler never crashes InterceptSuite. It is logged to your extension's output and the app carries on:

Method If it raises an error If it returns None
should_show_tab The tab is shown. Treated as False: the tab is hidden.
fetchdata The tab shows Error processing data: <message>. Converted to text.
updatedata The original packet is kept unchanged. "No change": the packet is left as it was.

Cleanup When an Extension Unloads

Optionally add a cleanup method to InterceptSuiteExtension. InterceptSuite calls it, with no arguments, when the extension is disabled, removed, or the app closes, so you can close files, sockets or threads.

class InterceptSuiteExtension:
    def register_interceptor_api(self, interceptor):
        interceptor.set_extension_name("My Extension")
        interceptor.set_extension_version("1.0.0")

    def cleanup(self):
        ExtensionLogger.Log("Goodbye")

Keep cleanup short. Unloading waits for it, and a slow one is logged with how long it took.

How Extensions Are Loaded

  • InterceptSuiteExtension is created once, with no arguments, when the extension loads. register_interceptor_api is called right after.
  • Extensions run inside the Python you configured in Extension > Settings, so any package installed there can be imported. The application folder is added to sys.path.
  • All extension code runs one call at a time, so extensions never run concurrently and you need no locking.
  • A file that has no InterceptSuiteExtension class, or whose class has no register_interceptor_api, is not loaded, and the reason is logged.

Checking Scope

Call is_in_scope(host, protocol) on the interceptor object to ask whether a target is in the user's Scope. Pass the same host string Proxy History shows and one of TCP, TLS, UDP, DTLS, QUIC. With no Scope entries configured, everything counts as in scope.

class MyHandler:
    def __init__(self, interceptor):
        self.interceptor = interceptor

    def should_show_tab(self, data):
        host = data.get("host") or data.get("destination_ip")
        host = f"{host}:{data['destination_port']}"
        return self.interceptor.is_in_scope(host, data["protocol"])

Application Version

interceptor.get_interceptsuite_version() returns the InterceptSuite version as major.minor.build, for extensions that need a minimum version.

Threading

Handlers for read-only views (Proxy History, received Replay packets) run on a background thread, so a slow extension shows "Processing..." and the UI stays responsive. Handlers for editable views run immediately so your updatedata result is ready when the user forwards or sends. Extension code never runs concurrently, so you do not need your own locking.

Complete Example: Base64 Viewer Extension

import base64
from InterceptSuite.Extensions.APIs.Logging import ExtensionLogger


class Base64HandlerClass:
    def should_show_tab(self, data):
        raw = data.get("data", "")
        try:
            base64.b64decode(raw, validate=True)
            return True
        except Exception:
            return False

    def fetchdata(self, data):
        raw = data.get("data", "")
        try:
            return base64.b64decode(raw).decode("utf-8", errors="ignore")
        except Exception:
            return "Unable to decode as Base64"

    def updatedata(self, data):
        edited = data.get("edited_data", "")
        if edited:
            try:
                return base64.b64encode(edited.encode("utf-8")).decode("utf-8")
            except Exception:
                return None
        return None


class InterceptSuiteExtension:
    def register_interceptor_api(self, interceptor):
        interceptor.set_extension_name("Base64 Viewer")
        interceptor.set_extension_version("1.0.0")
        interceptor.AddDataViewerTab("Base64", Base64HandlerClass())
        ExtensionLogger.Log("Base64 Viewer tab registered.")

Example Extensions Repository

For more working examples and starter templates, see the official repository:

github.com/InterceptSuite/Extension

The repository includes:

  • Working examples for common extension patterns
  • Starter templates for custom tab implementations
  • Real-world examples following best practices
  • Data format examples (Base64, JSON, XML, binary)