Fix audit findings: security, types, KID, retry, and version alignment
- Fix critical OpenCode plugin: read private key from file instead of embedding in JS - Add default KID (agent_id + '-key-1') matching Node and Go SDKs - Fix Operation.subject and Operation.action types from str to Dict[str, Any] - Fix retry off-by-one to give max_retries+1 total attempts - Fix OpenCode operation_type from 'tool_use' to 'ai.tool_use' - Update plugin path to .config/opencode/plugins matching Node SDK Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
21c63dbb28
commit
347d51febf
4 files changed
+18
-24
No files matched your search
+1
-1
@@ -46,7 +46,7 @@ from .types import (
|
|||||||
HealthResponse,
|
HealthResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
__version__ = "1.0.0"
|
__version__ = "1.1.0"
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"ElydoraClient",
|
"ElydoraClient",
|
||||||
|
|||||||
+4
-4
@@ -72,7 +72,7 @@ class ElydoraClient:
|
|||||||
self.token = token
|
self.token = token
|
||||||
|
|
||||||
self._prev_chain_hash = GENESIS_CHAIN_HASH
|
self._prev_chain_hash = GENESIS_CHAIN_HASH
|
||||||
self._kid = ""
|
self._kid = agent_id + '-key-1'
|
||||||
self._session = requests.Session()
|
self._session = requests.Session()
|
||||||
|
|
||||||
def set_kid(self, kid: str) -> None:
|
def set_kid(self, kid: str) -> None:
|
||||||
@@ -106,14 +106,14 @@ class ElydoraClient:
|
|||||||
hdrs = headers or self._headers()
|
hdrs = headers or self._headers()
|
||||||
|
|
||||||
last_exc: Optional[Exception] = None
|
last_exc: Optional[Exception] = None
|
||||||
for attempt in range(self.max_retries):
|
for attempt in range(0, self.max_retries + 1):
|
||||||
try:
|
try:
|
||||||
resp = self._session.request(
|
resp = self._session.request(
|
||||||
method, url, json=json_body, params=params, headers=hdrs, timeout=30
|
method, url, json=json_body, params=params, headers=hdrs, timeout=30
|
||||||
)
|
)
|
||||||
# Retry on 429 or 5xx
|
# Retry on 429 or 5xx
|
||||||
if resp.status_code == 429 or resp.status_code >= 500:
|
if resp.status_code == 429 or resp.status_code >= 500:
|
||||||
if attempt < self.max_retries - 1:
|
if attempt < self.max_retries:
|
||||||
retry_after = resp.headers.get("Retry-After")
|
retry_after = resp.headers.get("Retry-After")
|
||||||
if retry_after and retry_after.isdigit():
|
if retry_after and retry_after.isdigit():
|
||||||
delay = int(retry_after)
|
delay = int(retry_after)
|
||||||
@@ -126,7 +126,7 @@ class ElydoraClient:
|
|||||||
raise
|
raise
|
||||||
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as exc:
|
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as exc:
|
||||||
last_exc = exc
|
last_exc = exc
|
||||||
if attempt < self.max_retries - 1:
|
if attempt < self.max_retries:
|
||||||
time.sleep(min(2 ** attempt, 8))
|
time.sleep(min(2 ** attempt, 8))
|
||||||
continue
|
continue
|
||||||
raise
|
raise
|
||||||
|
|||||||
+11
-17
@@ -10,8 +10,8 @@ import sys
|
|||||||
from .base import AgentPlugin, InstallConfig, PluginStatus
|
from .base import AgentPlugin, InstallConfig, PluginStatus
|
||||||
|
|
||||||
|
|
||||||
PLUGIN_DIR = os.path.join(os.path.expanduser("~"), ".opencode", "plugins")
|
PLUGIN_DIR = os.path.join(os.path.expanduser("~"), ".config", "opencode", "plugins")
|
||||||
PLUGIN_FILENAME = "elydora-audit.js"
|
PLUGIN_FILENAME = "elydora-audit.mjs"
|
||||||
ELYDORA_DIR = os.path.join(os.path.expanduser("~"), ".elydora")
|
ELYDORA_DIR = os.path.join(os.path.expanduser("~"), ".elydora")
|
||||||
|
|
||||||
|
|
||||||
@@ -62,11 +62,7 @@ class OpenCodePlugin(AgentPlugin):
|
|||||||
pass # chmod may fail on Windows
|
pass # chmod may fail on Windows
|
||||||
|
|
||||||
js_content = _generate_js_plugin(
|
js_content = _generate_js_plugin(
|
||||||
org_id=org_id,
|
|
||||||
agent_id=agent_id,
|
agent_id=agent_id,
|
||||||
private_key=private_key,
|
|
||||||
kid=kid,
|
|
||||||
base_url=base_url,
|
|
||||||
guard_script_path=guard_script_path,
|
guard_script_path=guard_script_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -101,11 +97,7 @@ class OpenCodePlugin(AgentPlugin):
|
|||||||
|
|
||||||
def _generate_js_plugin(
|
def _generate_js_plugin(
|
||||||
*,
|
*,
|
||||||
org_id: str,
|
|
||||||
agent_id: str,
|
agent_id: str,
|
||||||
private_key: str,
|
|
||||||
kid: str,
|
|
||||||
base_url: str,
|
|
||||||
guard_script_path: str = "",
|
guard_script_path: str = "",
|
||||||
) -> str:
|
) -> str:
|
||||||
guard_path_escaped = guard_script_path.replace("\\", "\\\\") if guard_script_path else ""
|
guard_path_escaped = guard_script_path.replace("\\", "\\\\") if guard_script_path else ""
|
||||||
@@ -121,13 +113,15 @@ const path = require("path");
|
|||||||
const os = require("os");
|
const os = require("os");
|
||||||
const {{ spawnSync }} = require("child_process");
|
const {{ spawnSync }} = require("child_process");
|
||||||
|
|
||||||
const ORG_ID = {_js_str(org_id)};
|
const CONFIG_DIR = path.join(os.homedir(), ".elydora", {_js_str(agent_id)});
|
||||||
const AGENT_ID = {_js_str(agent_id)};
|
const CONFIG = JSON.parse(fs.readFileSync(path.join(CONFIG_DIR, "config.json"), "utf-8"));
|
||||||
const PRIVATE_KEY = {_js_str(private_key)};
|
const PRIVATE_KEY = fs.readFileSync(path.join(CONFIG_DIR, "private.key"), "utf-8").trim();
|
||||||
const KID = {_js_str(kid)};
|
const ORG_ID = CONFIG.org_id;
|
||||||
const BASE_URL = {_js_str(base_url)};
|
const AGENT_ID = CONFIG.agent_id;
|
||||||
|
const KID = CONFIG.kid;
|
||||||
|
const BASE_URL = CONFIG.base_url;
|
||||||
|
|
||||||
const CHAIN_STATE_PATH = path.join(os.homedir(), ".elydora", AGENT_ID, "chain-state.json");
|
const CHAIN_STATE_PATH = path.join(CONFIG_DIR, "chain-state.json");
|
||||||
|
|
||||||
function base64urlEncode(buf) {{
|
function base64urlEncode(buf) {{
|
||||||
return Buffer.from(buf).toString("base64url");
|
return Buffer.from(buf).toString("base64url");
|
||||||
@@ -262,7 +256,7 @@ module.exports = {{
|
|||||||
issued_at: issuedAt,
|
issued_at: issuedAt,
|
||||||
ttl_ms: 30000,
|
ttl_ms: 30000,
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
operation_type: "tool_use",
|
operation_type: "ai.tool_use",
|
||||||
subject: {{ type: "tool", id: toolName }},
|
subject: {{ type: "tool", id: toolName }},
|
||||||
action: {{ type: "execute", tool: toolName }},
|
action: {{ type: "execute", tool: toolName }},
|
||||||
payload: payload,
|
payload: payload,
|
||||||
|
|||||||
+2
-2
@@ -79,8 +79,8 @@ class Operation(TypedDict):
|
|||||||
issued_at: int
|
issued_at: int
|
||||||
ttl_ms: int
|
ttl_ms: int
|
||||||
nonce: str
|
nonce: str
|
||||||
subject: str
|
subject: Dict[str, Any]
|
||||||
action: str
|
action: Dict[str, Any]
|
||||||
payload_hash: str
|
payload_hash: str
|
||||||
prev_chain_hash: str
|
prev_chain_hash: str
|
||||||
chain_hash: str
|
chain_hash: str
|
||||||
|
|||||||
Reference in new issue
Block a user