PhishingPage ( Search )

1. Overview

The PhishingPage module implements BastionGuard’s interactive phishing analysis page using GTKmm (GTK4). It provides a compact analysis workflow composed of a page title, an input/action card for URL submission, a scrollable result viewer, and a status label for real-time feedback.

The page is designed as a remote-analysis client: it accepts a URL from the user, sends it to the BastionGuard Security Intelligence endpoint, downloads the resulting HTML report, parses relevant indicators from the returned page, and renders a normalized text summary inside the UI.

The module integrates with:

  • GTKmm / GTK4 – layout containers, labels, text view, scrolled window, and widget lifecycle
  • glibmm i18n – localized labels and runtime messages through _()
  • glibmm main loop – deferred UI updates via Glib::signal_idle()
  • Gio::Subprocess – remote HTML retrieval through an external curl subprocess
  • std::regex – structured extraction of phishing analysis fields from returned HTML
  • Background threading – asynchronous remote analysis without blocking the GTK main thread
  • Phishing input card component – emits signal_analyze_requested() and receives visual feedback updates

2. Page Construction and Layout

2.1 Base Container

PhishingPage derives from Gtk::Box and is initialized as a vertical container with spacing 14:

PhishingPage::PhishingPage()
    : Gtk::Box(Gtk::Orientation::VERTICAL, 14)

The page applies the CSS class:

phishing-page

and is configured with symmetric margins and full expansion in both directions.

2.2 Main Visual Sections

The page is composed of four primary UI sections:

  • Title label – a bold, large heading reading Phishing Scanner
  • Analysis card – an interactive card widget used to request URL analysis
  • Scrollable result area – a read-only text view used to display normalized analysis output
  • Status label – a compact runtime state indicator shown below the result viewer

The title label uses Pango markup and applies the CSS class:

page-title

The result scroller uses:

phishing-result-box

The embedded text view uses:

phishing-result-text

The status label uses:

phishing-status

Both the result buffer and status label are initialized to:

Ready.

3. Analyze Workflow and Signal Binding

3.1 Analyze Request Signal

The phishing analysis workflow starts from the embedded card widget. During construction, the page connects the card’s analyze signal to:

PhishingPage::on_analyze_requested

This method receives the user-provided URL as a Glib::ustring and becomes the main orchestration point for the remote lookup and parsing process.

3.2 Immediate UI Feedback

When a request begins, the page updates all visible UI state immediately:

  • The card feedback is set to Analysis started...
  • The status label is set to Analyzing URL...
  • The output area is reset with a line showing the URL being analyzed

This gives the user immediate confirmation before the background network operation starts.

3.3 Background Execution Model

The actual network request is executed in a detached background thread to prevent blocking the GTK main loop:

std::thread([this, url]() { ... }).detach();

The target endpoint is constructed by appending the URL-encoded user input to the BastionGuard Security Intelligence query parameter:

https://bastionguard.eu/bastionguard-security-intelligence/?q=<encoded-url>

Once the HTML is downloaded and parsed, all widget updates are marshaled back to the GTK main loop through Glib::signal_idle().connect_once(...).


4. Remote Fetch and URL Handling

4.1 Input Normalization Helpers

The module provides several internal helpers used during request construction and parsing:

  • trim_copy(s) – removes leading and trailing whitespace
  • url_encode(s) – percent-encodes arbitrary URL input for safe query-string transport
  • html_entity_decode(s) – decodes a limited set of common HTML entities
  • strip_tags(s) – strips HTML tags, decodes entities, compresses whitespace, and trims the result

The URL encoder preserves RFC-safe characters and percent-encodes all others in uppercase hexadecimal form.

4.2 HTML Download via curl

Remote content retrieval is implemented by fetch_url_body(url), which launches an external curl process through Gio::Subprocess.

The current invocation is equivalent to:

/usr/bin/curl -L --silent --show-error --fail --max-time 20 <url>

Behavioral characteristics:

  • -L follows redirects
  • --silent suppresses progress output
  • --show-error preserves diagnostic stderr on failure
  • --fail treats HTTP errors as command failure
  • --max-time 20 limits the remote fetch duration

If the subprocess exits successfully, the function returns the response body; otherwise it returns an empty string.


5. HTML Parsing Strategy

5.1 Parsed Result Model

The page parses remote HTML into a structured internal result object (ParsedResult). The extracted fields include:

  • classification
  • severity
  • normalized_host
  • evaluation_chain
  • matched_indicator
  • live_probe_status
  • live_probe_text
  • report_text

The final report_text is a normalized, human-readable summary generated from the extracted fields.

5.2 Regex-Based Extraction

The parser uses targeted regular expressions to extract specific blocks from the returned BastionGuard Security Intelligence HTML.

Field extraction is centralized through:

  • extract_first(text, regex, group) – returns the first matching capture group as an optional string
  • extract_all_li_after_label(html, label) – extracts list items from a labeled HTML block such as Analysis Details

The parser currently looks for structured fragments such as:

  • Classification inside a phishing-badge classification block
  • Severity inside phishing-sev-level
  • Normalized Host inside a labeled phishing-code block
  • Evaluation Chain inside a labeled phishing-code block
  • Matched Indicator inside a labeled phishing-code block
  • Live Probe Status inside a badge-style labeled block
  • Live Probe Details inside a styled informational <div>
  • Analysis Details inside a labeled unordered list

The parser is therefore tightly coupled to the current HTML structure of the BastionGuard remote analyzer page.

5.3 HTML Cleanup

Before values are returned to the UI, extracted fragments are normalized through:

  • HTML tag removal
  • Entity decoding
  • Whitespace collapsing
  • Trim normalization

This makes the final report independent from inline markup present in the remote response.


6. Result Rendering

6.1 Normalized Text Report

After extraction, the module composes a plain-text summary in a fixed reporting format.

The generated report may contain:

  • Classification
  • Severity
  • Normalized Host
  • Evaluation Chain
  • Matched Indicator
  • Live Probe
  • Live Probe Details
  • A bullet-style Analysis Details section

If a field is absent, it is either omitted or replaced with N/A in the case of the classification header.

6.2 Output Widget Behavior

The rendered report is written directly into the text buffer of txt_output_. The result view is configured as:

  • Read-only
  • Cursor hidden
  • Word-wrapped
  • Non-monospace

This makes the result area suitable for readable narrative output rather than raw log-style text.


7. Classification Logic and UI Feedback

7.1 Success Path

If parsing succeeds and a classification is found, the report is shown in the result area and the page updates the status/feedback according to the classification content.

The current logic checks for classification substrings:

  • If classification contains MALICIOUS, the UI reports a malicious result and marks the card with error-style feedback
  • If classification contains SUSPICIOUS, the UI reports a suspicious result and marks the card with error-style feedback
  • Otherwise, the UI reports generic completion and uses non-error feedback

7.2 Empty or Invalid Remote Response

If the HTML body is empty, the page reports that the BastionGuard Security Intelligence service could not be contacted.

In that case:

  • The card shows an error feedback message
  • The status label changes to Remote analysis failed.
  • The output area shows a message indicating that no valid HTML was returned

7.3 Unexpected HTML Format

If the remote page responds but the expected classification block is missing, the page treats the result as a parsing failure.

In that case:

  • The card shows Unexpected response format from remote analyzer.
  • The status label changes to Parsing failed.
  • The output area explains that no classification block was found

8. Lifecycle and Thread-Safety Considerations

8.1 Destruction Guard

The page maintains an internal destroyed_ flag. The destructor sets:

destroyed_ = true;

This guard is checked both inside the worker thread and inside idle callbacks before touching the UI, reducing the risk of use-after-destroy behavior when the page is closed during an in-flight request.

8.2 Main-Loop UI Updates

All UI mutations after the background fetch are routed through Glib::signal_idle().connect_once(...). This ensures GTK widgets are updated only from the main loop, preserving thread safety.

The network and parsing work itself remains offloaded to a detached worker thread.


9. Security and Runtime Considerations

  • Asynchronous execution: remote analysis is performed in a background thread to keep the UI responsive
  • Controlled network access: HTML retrieval is delegated to curl with redirect support, timeout enforcement, and failure-aware behavior
  • Bounded request time: the remote fetch uses --max-time 20 to reduce indefinite blocking risk
  • UI-thread safety: all widget updates are marshaled back to the GTK main thread
  • Graceful failure handling: transport failures and malformed remote responses are surfaced with explicit user feedback
  • HTML normalization: tags and common entities are stripped before display, producing a clean textual report
  • Structure-sensitive parsing: because the implementation relies on regex over HTML, changes to the remote page markup may require parser updates
  • Lifecycle robustness: the destroyed_ flag helps prevent invalid UI access after widget destruction