1. Overview
This module implements a Native Messaging Host used to bridge a browser extension (or embedded browser component) with BastionGuard’s local anti-phishing logic. It communicates over stdin/stdout using the Chrome/Chromium Native Messaging framing protocol (4-byte little-endian length + JSON payload).
The host provides a small command dispatcher with multiple actions, including:
- ping – connectivity/health check
- check_url – URL analysis against a locally stored phishing blacklist
- set_lang, restart_scanner, open_page – currently stubbed endpoints (placeholders for future integration)
The anti-phishing decision is computed locally by loading and caching a blacklist file and performing host-based matching with suffix fallbacks.
2. Dependencies
- nlohmann/json – JSON serialization and parsing
- C++ standard library – I/O streams, containers, filesystem, time, formatting
- POSIX –
unistd.h,wait.h,gmtime_r()(thread-safe UTC conversion)
3. Native Messaging Protocol
3.1 Message Framing
Each incoming request is read as:
- 4 bytes: unsigned 32-bit length (little-endian)
- Payload: JSON bytes of the specified length
Outgoing messages follow the same framing format.
3.2 read_exact(in, buf, n)
The helper reads exactly n bytes from an input stream, retrying until the buffer is filled or EOF is encountered.
This prevents partial reads from breaking message boundaries.
3.3 read_message()
Reads and parses a single framed message. Behavior:
- Reads length prefix
- Allocates a buffer of that size
- Reads the payload fully
- Parses JSON using
json::parse(..., false)(non-throwing parse)
If EOF is encountered during framing or payload read, the function throws an exception to be handled by the main loop.
3.4 send_message(j)
Serializes a JSON object to string, writes the 4-byte length prefix, then writes the JSON bytes to stdout and flushes.
This function ensures the browser-side extension receives a complete framed response.
4. Time Utilities
4.1 now_iso()
Generates an ISO-8601 UTC timestamp in the format:
YYYY-MM-DDTHH:MM:SSZ
Implementation details:
- Uses
std::chrono::system_clock - Converts to UTC via
gmtime_r() - Formats using
std::put_time()
This timestamp is attached to responses as ts and used for diagnostics (e.g., ping responses).
5. Blacklist Loading and Caching
5.1 Blacklist Location
The phishing blacklist is expected at:
~/.local/share/BastionGuard/data/phishing/blacklist.txt
The path is resolved using the HOME environment variable.
5.2 Cache Strategy
Blacklist entries are cached in:
static std::unordered_set<std::string> cached_blstatic std::time_t cached_bl_mtime– last modification time used to detect changes
The blacklist is reloaded only when:
- The file exists and its modification time differs from the cached value, or
- The cache is empty
This reduces disk I/O and improves throughput when the host processes many URL checks.
5.3 Normalization Rules
When loading the blacklist:
- Each line is trimmed
- Empty lines and comment lines (
#) are ignored - Entries are lowercased
- Entries are inserted into an unordered set to guarantee uniqueness
5.4 file_time_type to time_t Conversion
The module converts filesystem timestamps (std::filesystem::file_time_type) to time_t for simple cache comparisons.
This is done by translating the file clock into std::chrono::system_clock timepoints.
6. URL Host Extraction
6.1 extract_host_from_url(url)
Extracts a host component using lightweight parsing rules:
- Skips scheme prefix
://if present - Strips optional credentials (
user@) - Stops at
/ : ? #delimiters - Removes port suffix if present (
:443) - Trims and lowercases the host
This approach is intentionally minimal, designed for performance and compatibility with typical URLs observed by browser extensions.
7. Action Handlers
7.1 ping
Returns a simple health response:
ok: truepong_at: ISO timestamp
This is used to validate that the native host is running and responsive.
7.2 check_url
Performs local anti-phishing evaluation of a submitted URL.
Input
The request must contain:
{"action":"check_url","url":"https://example.com/..."}
If url is missing or not a string, the response returns ok:false with an error message.
Blacklist Decision Logic
After ensuring the blacklist is loaded, the module computes a verdict using the following strategy:
- Extract host from URL
- If host exists:
- Exact match: block if
hostequals a blacklist entry - Suffix match: block if
hostends with a blacklist entry (e.g.,login.bad.tldmatchesbad.tld)
- Exact match: block if
- If host extraction fails (empty host):
- Substring fallback: block if any entry appears as a substring inside the URL
The response includes an extra object containing:
block– boolean verdictreason– diagnostic string describing which rule triggered (or not)
Example response structure:
{
"ok": true,
"url": "https://bad.example/login",
"extra": {
"block": true,
"reason": "host_suffix: example"
},
"ts": "2026-01-21T17:25:27Z"
}
7.3 set_lang / restart_scanner / open_page (stubs)
These actions currently return:
{"ok": true}
They act as placeholders for future integration where the browser extension can request BastionGuard to change language, restart services, or open specific UI pages.
8. Request Dispatching
8.1 dispatch(req)
The dispatcher enforces basic request validity:
- Request must be a JSON object
- Must contain a string field
action
Unsupported actions produce:
{"ok": false, "error": "unknown action: <action>"}
9. Main Loop
The main process loop:
- Reads a request via
read_message() - Logs the request to stderr
- Dispatches it
- Attaches
tstimestamp - Sends response via
send_message() - Logs the response to stderr
If JSON parsing yields a discarded object, the loop terminates.
10. Error Handling
- Most handler-level errors return
ok:falseresponses with expliciterrorstrings - Fatal errors in the loop are caught and logged to stderr
- The host attempts to send a final fatal error response if possible
This design avoids silent failures and provides diagnostics for extension developers and system administrators.
11. Runtime and Security Considerations
- Trust boundary: all inbound JSON fields must be treated as untrusted input; the module enforces minimal validation for required fields
- Blacklist integrity: decisions rely on the local blacklist file; ensure it is generated/updated by trusted BastionGuard components
- Suffix matching risk: suffix checks can over-block if the blacklist includes overly generic entries; ensure list entries are normalized and curated
- Performance: caching avoids repeated file reads;
unordered_setlookup provides O(1) average-case exact matching - Logging: request/response are printed to stderr, which is helpful for debugging but should be reviewed for privacy impact if URLs may include sensitive query parameters
- Locale: current implementation logs using plain strings; future localization of error output should be consistent with BastionGuard logging policy
12. Extension Points
- Action completion: implement the stub actions by integrating with BastionGuard settings and service control mechanisms
- Robust URL parsing: replace host parsing with a hardened URL parser if required (while preserving performance)
- Policy model: extend
extrapayload to include confidence, source list version, and rule identifiers - Atomic blacklist reload: optionally use atomic file replace strategies and read-write locks for multi-threaded host variants