mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-09-04 16:36:35 +02:00
Clarify OpenClaw HMAC agent credentials
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
"""Regression coverage for OpenClaw skill HMAC environment names."""
|
||||||
|
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def _load_sb_query(monkeypatch):
|
||||||
|
module_path = Path(__file__).resolve().parents[2] / "openclaw-skills" / "shadowbroker" / "sb_query.py"
|
||||||
|
spec = importlib.util.spec_from_file_location("shadowbroker_skill_sb_query_test", module_path)
|
||||||
|
assert spec and spec.loader
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def test_openclaw_skill_prefers_hmac_secret_env(monkeypatch):
|
||||||
|
monkeypatch.setenv("SHADOWBROKER_HMAC_SECRET", "preferred-hmac-secret")
|
||||||
|
monkeypatch.setenv("SHADOWBROKER_KEY", "legacy-hmac-secret")
|
||||||
|
|
||||||
|
module = _load_sb_query(monkeypatch)
|
||||||
|
|
||||||
|
assert module.ShadowBrokerClient()._hmac_secret == "preferred-hmac-secret"
|
||||||
|
|
||||||
|
|
||||||
|
def test_openclaw_skill_accepts_legacy_key_as_hmac_secret_alias(monkeypatch):
|
||||||
|
monkeypatch.delenv("SHADOWBROKER_HMAC_SECRET", raising=False)
|
||||||
|
monkeypatch.setenv("SHADOWBROKER_KEY", "legacy-hmac-secret")
|
||||||
|
|
||||||
|
module = _load_sb_query(monkeypatch)
|
||||||
|
client = module.ShadowBrokerClient()
|
||||||
|
headers = client._sign_headers("GET", "/api/ai/tools")
|
||||||
|
|
||||||
|
assert client._hmac_secret == "legacy-hmac-secret"
|
||||||
|
assert "X-SB-Timestamp" in headers
|
||||||
|
assert "X-SB-Nonce" in headers
|
||||||
|
assert "X-SB-Signature" in headers
|
||||||
|
assert "Authorization" not in headers
|
||||||
|
assert "X-Admin-Key" not in headers
|
||||||
@@ -632,10 +632,18 @@ function ConnectModalBody({ apiEndpoint, handleCopy, copied }: ConnectModalBodyP
|
|||||||
const buildSnippet = (key: string) => {
|
const buildSnippet = (key: string) => {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
if (resolvedUrl) lines.push(`SHADOWBROKER_URL=${resolvedUrl}`);
|
if (resolvedUrl) lines.push(`SHADOWBROKER_URL=${resolvedUrl}`);
|
||||||
lines.push(`SHADOWBROKER_KEY=${key}`);
|
lines.push(`SHADOWBROKER_HMAC_SECRET=${key}`);
|
||||||
lines.push(`SHADOWBROKER_ACCESS=${accessTier}`);
|
lines.push(`SHADOWBROKER_ACCESS=${accessTier}`);
|
||||||
if (connectionMode === 'remote' && nodeId) lines.push(`SHADOWBROKER_NODE_ID=${nodeId}`);
|
if (connectionMode === 'remote' && nodeId) lines.push(`SHADOWBROKER_NODE_ID=${nodeId}`);
|
||||||
lines.push('');
|
lines.push('');
|
||||||
|
lines.push('# AUTH DIRECTIVE:');
|
||||||
|
lines.push('# SHADOWBROKER_HMAC_SECRET is a shared HMAC signing secret, not a raw API key.');
|
||||||
|
lines.push('# Never send it as X-Admin-Key, Authorization: Bearer, a query parameter, or a plain request header.');
|
||||||
|
lines.push('# Every direct ShadowBroker API request must be HMAC-SHA256 signed with:');
|
||||||
|
lines.push('# X-SB-Timestamp, X-SB-Nonce, X-SB-Signature');
|
||||||
|
lines.push('# Signature input: METHOD|path|timestamp|nonce|sha256(body)');
|
||||||
|
lines.push('# Use the ShadowBrokerClient/openclaw skill helper so requests are signed automatically.');
|
||||||
|
lines.push('');
|
||||||
lines.push('# OPERATING DIRECTIVE:');
|
lines.push('# OPERATING DIRECTIVE:');
|
||||||
lines.push('# You are a remote OpenClaw agent connected to ShadowBroker.');
|
lines.push('# You are a remote OpenClaw agent connected to ShadowBroker.');
|
||||||
lines.push('# ShadowBroker is a live intelligence and telemetry platform, not a narrow single-purpose API.');
|
lines.push('# ShadowBroker is a live intelligence and telemetry platform, not a narrow single-purpose API.');
|
||||||
|
|||||||
@@ -129,13 +129,16 @@ const OnboardingModal = React.memo(function OnboardingModal({
|
|||||||
|
|
||||||
const agentSnippet = [
|
const agentSnippet = [
|
||||||
`SHADOWBROKER_URL=${agentEndpoint}`,
|
`SHADOWBROKER_URL=${agentEndpoint}`,
|
||||||
agentSecret ? `SHADOWBROKER_KEY=${agentSecret}` : 'SHADOWBROKER_KEY=<generate in ShadowBroker>',
|
agentSecret ? `SHADOWBROKER_HMAC_SECRET=${agentSecret}` : 'SHADOWBROKER_HMAC_SECRET=<generate in ShadowBroker>',
|
||||||
`SHADOWBROKER_ACCESS=${agentTier}`,
|
`SHADOWBROKER_ACCESS=${agentTier}`,
|
||||||
'',
|
'',
|
||||||
'# FIRST: load available tools',
|
'# FIRST: load available tools',
|
||||||
`GET ${agentEndpoint}/api/ai/tools`,
|
`GET ${agentEndpoint}/api/ai/tools`,
|
||||||
'',
|
'',
|
||||||
'# Auth: HMAC-SHA256 signed requests.',
|
'# Auth: SHADOWBROKER_HMAC_SECRET is not a raw API key.',
|
||||||
|
'# Sign every direct request with X-SB-Timestamp, X-SB-Nonce, and X-SB-Signature.',
|
||||||
|
'# Signature input: METHOD|path|timestamp|nonce|sha256(body).',
|
||||||
|
'# Do not send the secret as X-Admin-Key, Authorization, or a query parameter.',
|
||||||
'# Restricted = read-only telemetry. Full = can write when asked.',
|
'# Restricted = read-only telemetry. Full = can write when asked.',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
const remoteAgentNeedsTor = agentMode === 'remote' && !torAddress;
|
const remoteAgentNeedsTor = agentMode === 'remote' && !torAddress;
|
||||||
|
|||||||
@@ -37,7 +37,18 @@ SHADOWBROKER_HMAC_SECRET=your-hmac-secret-here
|
|||||||
```
|
```
|
||||||
|
|
||||||
The HMAC secret is found in ShadowBroker's **Connect OpenClaw** modal (AI Intel panel).
|
The HMAC secret is found in ShadowBroker's **Connect OpenClaw** modal (AI Intel panel).
|
||||||
All requests are automatically signed with HMAC-SHA256 (timestamp + nonce + body digest) for replay protection and request-body integrity binding.
|
`SHADOWBROKER_HMAC_SECRET` is a shared signing secret, not a raw API key. Do not
|
||||||
|
send it as `X-Admin-Key`, `Authorization: Bearer`, a query parameter, or any
|
||||||
|
plain request header. The `ShadowBrokerClient` signs every direct request with
|
||||||
|
`X-SB-Timestamp`, `X-SB-Nonce`, and `X-SB-Signature` using:
|
||||||
|
|
||||||
|
```text
|
||||||
|
HMAC-SHA256(secret, METHOD|path|timestamp|nonce|sha256(body))
|
||||||
|
```
|
||||||
|
|
||||||
|
For compatibility with older snippets, `SHADOWBROKER_KEY` is also accepted by
|
||||||
|
the client as the same HMAC signing secret. Prefer `SHADOWBROKER_HMAC_SECRET`
|
||||||
|
for new setups.
|
||||||
|
|
||||||
### SSE Stream (Preferred — Low-Latency Push)
|
### SSE Stream (Preferred — Low-Latency Push)
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ the ShadowBroker OSINT platform.
|
|||||||
|
|
||||||
For local access (same machine), no authentication is needed.
|
For local access (same machine), no authentication is needed.
|
||||||
For remote access, set SHADOWBROKER_HMAC_SECRET to enable HMAC-signed requests.
|
For remote access, set SHADOWBROKER_HMAC_SECRET to enable HMAC-signed requests.
|
||||||
|
Older ShadowBroker UI snippets used SHADOWBROKER_KEY; this client still accepts
|
||||||
|
that value as an HMAC signing secret for compatibility. Never send either value
|
||||||
|
as a raw bearer token, X-Admin-Key, query parameter, or unsigned header.
|
||||||
|
|
||||||
Usage (inside an OpenClaw skill):
|
Usage (inside an OpenClaw skill):
|
||||||
from sb_query import ShadowBrokerClient
|
from sb_query import ShadowBrokerClient
|
||||||
@@ -43,11 +46,17 @@ class ShadowBrokerClient:
|
|||||||
|
|
||||||
Supports both local (no auth) and remote (HMAC-signed) connections.
|
Supports both local (no auth) and remote (HMAC-signed) connections.
|
||||||
Set SHADOWBROKER_HMAC_SECRET env var to enable remote authentication.
|
Set SHADOWBROKER_HMAC_SECRET env var to enable remote authentication.
|
||||||
|
SHADOWBROKER_KEY is accepted only as a backwards-compatible HMAC-secret
|
||||||
|
alias for older copy snippets.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, base_url: str = SB_BASE, hmac_secret: str = ""):
|
def __init__(self, base_url: str = SB_BASE, hmac_secret: str = ""):
|
||||||
self.base = base_url.rstrip("/")
|
self.base = base_url.rstrip("/")
|
||||||
self._hmac_secret = hmac_secret or os.environ.get("SHADOWBROKER_HMAC_SECRET", "")
|
self._hmac_secret = (
|
||||||
|
hmac_secret
|
||||||
|
or os.environ.get("SHADOWBROKER_HMAC_SECRET", "")
|
||||||
|
or os.environ.get("SHADOWBROKER_KEY", "")
|
||||||
|
)
|
||||||
self._client = None
|
self._client = None
|
||||||
# Version tracking for incremental updates
|
# Version tracking for incremental updates
|
||||||
self._last_data_version: int | None = None
|
self._last_data_version: int | None = None
|
||||||
|
|||||||
Reference in New Issue
Block a user