T09 · Insecure Skill Coding Practices
- Location
src/tools/local_storage.rs:40- Finding
JavaScript Injection Through Local-Storage Tool Parameters
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This browser-automation skill has a legitimate core purpose, but it exposes high-impact browser control and session-cookie handling with weak scoping, unsafe defaults, and abuse-oriented documentation.
Review carefully before installing. Use only with an isolated browser profile or test accounts, avoid real financial or administrative sessions, do not use it to bypass site protections, and treat saved session files as secrets equivalent to login tokens. The package should narrow triggers and permissions, keep Chrome sandboxing enabled by default, harden cookie/session storage, and add explicit confirmations or warnings for sensitive browser actions.
src/tools/local_storage.rs:40JavaScript Injection Through Local-Storage Tool Parameters
src/bin/cli.rs:242Authentication Cookies Stored in Plaintext with Default File Permissions
src/bin/cli.rs:144Chrome Sandbox Systematically Disabled by CLI Workflows
The staged login-then-reuse flow operationalizes authenticated session takeover by instructing users to export a live browser session and replay it later. In the wrong context, this is effectively credential/session token capture and reuse, enabling persistent unauthorized access if the session file is obtained by another party or used beyond the original authorization.
The trigger phrases are very broad and match common user intents such as browsing, searching, or opening a browser. Because the skill also requests both network and filesystem permissions, accidental or silent activation could cause the agent to perform web actions or local file interactions in contexts where the user did not specifically intend to invoke this skill.
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
ges to analyze for structure (default: 5)
#[arg(long, default_value = "5")]
max_pages: usize,
/// Maximum number of sitemaps to parse (default: 10)
#[arg(long, default_value = "10")]
max_sitemaps: usize,
/// Output file (JSON)
#[arg(long)]
output: Option<PathBuf>,
},
}
#[derive(Serialize, Deserialize)]
struct SessionData {
cookies: Vec<headless_chrome::protocol::cdp::Network::Cookie>,
// Local storage could be added here
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let cli = Cli::parse();
match cli.command {
Commands::Navigate { url, human_emulation, wait_for_selector, load_session } => {
info!("Navigating to: {}", url);
let options = LaunchOptions::default().sandbox(false);
let session = BrowserSession::launch(optio
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
return true;
})()
"#;
self.tab()?
.evaluate(go_forward_js, false)
.map_err(|e| BrowserError::NavigationFailed(format!("Failed to go forward: {}", e)))?;
// Wait a moment for navigation
std::thread::sleep(std::time::Duration::from_millis(300));
Ok(())
}
/// Get cookies from the current session
pub fn get_cookies(&self) -> Result<Vec<headless_chrome::protocol::cdp::Network::Cookie>> {
self.tab()?
.get_cookies()
.map_err(|e| BrowserError::ChromeError(format!("Failed to get cookies: {}", e)))
}
/// Set cookies for the current session
pub fn set_cookies(&self, cookies: Vec<CookieParam>) -> Result<()> {
let tab = self.tab()?;
for cookie in cookies {
// Convert CookieParam to headless_chrome::protocol::cdp::Network::CookieParam
let param = CdpCookieParam {
name: cookie.name,
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).
/// Get a reference to the browser session (blocking lock)
pub(crate) fn session(&self) -> std::sync::MutexGuard<'_, BrowserSession> {
self.session.lock().expect("Failed to lock browser session")
}
}
impl Default for BrowserServer {
fn default() -> Self {
Self::new().expect("Failed to create default browser server")
}
}
impl Drop for BrowserServer {
fn drop(&mut self) {
debug!("BrowserServer dropped");
}
}
#[tool_handler]
impl ServerHandler for BrowserServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {
instructions: Some("Browser-use MCP Server".into()),
capabilities: ServerCapabilities::builder().enable_tools().build(),
..Default::default()
}
}
}
The guidance tells users how to run a browser automation MCP server but omits any safety notice that the server can drive a real browser, execute page JavaScript, access authenticated sessions, and interact with external websites. In an agent-skill context, that omission can cause operators or downstream agents to treat it as a harmless local utility, increasing the chance of unsafe deployment and unintended data or network exposure.
The README explicitly promotes an AI-driven browser automation MCP server but does not warn users that the tool can navigate sites, click elements, input data, and access page content on their behalf. In an agent context, this omission increases the risk of unintended actions against authenticated sessions, sensitive pages, or user data because operators may enable the server without understanding its access and automation scope.
Without declared permissions the skill's intent is opaque and cannot be validated.
The recipe explicitly promotes 'bypass bot detection' on protected sites through human-emulation behavior, which is an evasion tactic intended to circumvent defensive controls. While this is documentation rather than executable malware, publishing anti-detection guidance materially increases the skill's abuse potential for unauthorized scraping or account automation.
The documentation labels saved-session reuse as a 'cookie heist' and presents a workflow for persisting authentication state to a file for later reuse. Even if framed as convenience, this encourages handling session artifacts like bearer credentials, which can be copied, exfiltrated, or reused to impersonate a logged-in user.
The skill describes saving and reloading authenticated browser sessions without any warning that auth.json may contain cookies or other secrets equivalent to account access. Users may treat the file as harmless automation state, increasing the chance of credential leakage, privilege misuse, or unauthorized replay.
Mixing Chinese and English triggers without documenting language scope or opt-in increases the activation surface across multiple language contexts. In a skill that automates browser actions and has network plus filesystem permissions, this makes unintended invocation more likely and can lead to actions being performed on behalf of users who did not realize this skill was being selected.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
});
// Start HTTP server with SSE router
let listener = tokio::net::TcpListener::bind(&bind_addr).await?;
axum::serve(listener, router.into_make_service()).await?;
}
Transport::Http => {
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
});
// Start HTTP server with SSE router
let listener = tokio::net::TcpListener::bind(&bind_addr).await?;
axum::serve(listener, router.into_make_service()).await?;
}
Transport::Http => {
This code registers event listeners that collect console API output and log entries from visited pages into in-memory session state. Although the behavior is commented for developers, there is no user-facing warning, confirmation, or disclosure in this file that the skill captures page-generated console data, which may include sensitive information emitted by web applications.
Connecting to an existing browser over a supplied WebSocket URL can attach this code to a remote browser session that may have access to authenticated pages, cookies, and internal network resources. If untrusted input can influence the ws_url, this becomes a powerful remote-control capability with SSRF-like and session-hijacking implications.
The session exposes direct cookie read and write primitives, which can access authentication state and impersonate users if misused. In an agent/browser automation context, this is especially sensitive because cookies are a primary bearer credential and these methods provide no guardrails, scoping checks, or user-consent boundary.
The extractor intentionally serializes live values from INPUT and TEXTAREA elements into the returned DOM snapshot, which can capture passwords, PII, session-related data, draft messages, and other sensitive user content. In an agent/browser-automation skill, this is especially dangerous because the snapshot is designed for AI consumption and may be logged, transmitted, or exposed to downstream components beyond the page context.
This file exposes a tool named browser_evaluate that executes arbitrary JavaScript in the browser context, and there are no visible safeguards, capability restrictions, or user-facing warnings at the MCP boundary. In an agent skill, this increases risk because untrusted prompts or tool invocations could run active code in loaded pages, enabling DOM manipulation, data extraction, token theft from page context, or triggering unintended actions on authenticated sites.
This tool writes screenshot bytes to an arbitrary path supplied in params.path with no validation, sandboxing, or confirmation. In an agent context, that enables unintended file overwrite or writing sensitive captured browser content to attacker-influenced locations, which is especially risky because screenshots may contain credentials, session data, or private page contents.
The tool executes element.click() on a page, which can submit forms, trigger purchases, navigation, or other irreversible UI actions. In this file there is no confirmation prompt, user-facing log/print, or warning comment/docstring explaining that the tool may cause state-changing actions.
This code performs a potentially destructive operation by closing the current browser session or all tabs, but the implementation provides no confirmation prompt, visible user-facing warning before execution, or explanatory comment/docstring aimed at informing the user of the impact. The success message appears only after the action completes, so it does not serve as disclosure before a potentially disruptive state change.
The implementation and comments indicate the tool may only close tabs or perform a partial shutdown, yet it reports that the browser was closed successfully. This creates a state-integrity issue where callers may make security or workflow decisions based on a false assumption that the browser session no longer exists, potentially leaving authenticated pages, session state, or automation context still active.
The tool schema documents an optional URL filter for cookie retrieval, but the implementation ignores that parameter and always returns all session cookies. In a browser-automation skill, cookies are highly sensitive authentication material, so this mismatch can cause overbroad disclosure to callers that reasonably expect least-privilege access scoped to specific URLs.
get_cookies returns raw session cookies directly from the browser context without any warning, consent check, or minimization. In this skill context, those values can include active session tokens and cross-site authentication artifacts, making exfiltration of user accounts or impersonation straightforward if the tool is exposed to untrusted prompts or agents.
Detected: suspicious.dynamic_code_execution