Back to skill

Security audit

Microsoft 365 Graph Openclaw

Security checks for vulnerabilities and agentic risk

Overview

This Microsoft 365/OpenClaw skill is coherent, but its broad persistent Microsoft 365 access, privileged setup scripts, background services, and credential-handling weaknesses require manual review before use.

Install only if you are comfortable granting persistent read/write access to mail, calendar, contacts, and OneDrive under the authenticated Microsoft account. Before running setup, review the sudo scripts, prefer a tenant-owned app registration, restrict token file permissions, avoid anonymous/edit share links unless intentional, rotate any tokens printed into logs, and run the webhook services on a host where systemd, journal, and process visibility are appropriately locked down.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/utils.py:13
Finding
OAuth access and refresh tokens are stored without restrictive filesystem permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils.py:13-16` and `scripts/utils.py:40-42` **Vulnerability Type**: Insecure storage of OAuth credentials **Risk Level**: High ### Vulnerable Code ```python STATE_DIR = WORKSPACE_ROOT / "state" STATE_DIR.mkdir(exist_ok=True) AUTH_FILE = STATE_DIR / "graph_auth.json" LOG_FILE = STATE_DIR / "graph_ops.log" ``` ```python def save_auth_state(data: Dict[str, Any]) -> None: with AUTH_FILE.open("w", encoding="utf-8") as f: json.dump(data, f, indent=2) ``` ### Technical Analysis The authentication state contains Microsoft Graph access and refresh tokens. The code creates the state directory and authentication file using process-default permissions rather than explicitly enforcing owner-only access. Under a common `umask` of `022`, a newly created file can receive mode `0644`, making it readable by other local users. The containing directory may similarly be created with mode `0755`. A refresh token is particularly sensitive because it can be exchanged repeatedly for new access tokens until revoked. The implementation also writes directly to the final path instead of using a protected temporary file followed by an atomic replacement. It does not verify that the destination is owned by the expected user or reject symbolic links. ### Attack Path 1. A user completes the device-code login flow. 2. `save_auth_state()` writes the access token and refresh token to `state/graph_auth.json`. 3. The host has a permissive default `umask`, resulting in a file readable by another local account. 4. The local attacker reads the authentication file. 5. The attacker submits the refresh token to the Microsoft identity token endpoint using the recorded client and tenant identifiers. 6. The attacker receives a valid Graph access token and invokes APIs covered by the granted scopes. ### Impact Assessment A successful attacker can obtain the same delegated Microsoft Graph privileges as the authenticated user ...[truncated 491 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create the state directory with mode `0700`. - Create the authentication file with mode `0600`, independent of the caller's `umask`. - Write credentials atomically through a same-directory temporary file opened with exclusive, owner-only permissions. - Validate that the state directory and credential file are owned by the current user. - Reject symbolic-link destinations and unexpected non-regular files. - Consider using the operating system credential store or a dedicated secrets manager. Example hardening approach: ```python STATE_DIR.mkdir(mode=0o700, parents=True, exist_ok=True) os.chmod(STATE_DIR, 0o700) def save_auth_state(data: Dict[str, Any]) -> None: temp_path = AUTH_FILE.with_suffix(".tmp") fd = os.open(temp_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) try: with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(data, f, indent=2) f.flush() os.fsync(f.fileno()) os.replace(temp_path, AUTH_FILE) os.chmod(AUTH_FILE, 0o600) finally: if temp_path.exists(): temp_path.unlink() ``` Existing installations should immediately change permissions on `state/` and `state/graph_auth.json` and rotate tokens if unauthorized local access may have occurred. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/utils.py:108
Finding
Graph bearer token can be transmitted to an unrestricted URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils.py:108-127` and `scripts/drive_ops.py:133-140` **Vulnerability Type**: OAuth bearer-token disclosure through insufficient destination validation **Risk Level**: High ### Vulnerable Code ```python def authorized_request(method: str, url: str, **kwargs) -> requests.Response: headers = kwargs.pop("headers", {}) headers["Authorization"] = f"Bearer {get_access_token()}" headers.setdefault("Accept", "application/json") if "json" in kwargs and "Content-Type" not in headers: headers["Content-Type"] = "application/json" kwargs["headers"] = headers resp = requests.request(method, url, timeout=60, **kwargs) if resp.status_code == 401: # token might be expired; refresh once refresh_access_token(force=True) headers["Authorization"] = f"Bearer {get_access_token()}" resp = requests.request(method, url, timeout=60, **kwargs) resp.raise_for_status() return resp def graph_url(path: str) -> str: if path.startswith("http"): return path if not path.startswith("/"): path = "/" + path return GRAPH_BASE_URL + path ``` ```python def download_file(item_id: str, remote: str, local: Path) -> None: if not item_id: if not remote: raise SystemExit("Provide --item-id or --remote.") item_id = resolve_item_path(remote) metadata = authorized_request("GET", graph_url(f"/me/drive/items/{item_id}")) download_url = metadata.json()["@microsoft.graph.downloadUrl"] resp = authorized_request("GET", download_url) local.write_bytes(resp.content) ``` ### Technical Analysis `authorized_request()` unconditionally attaches the Microsoft Graph bearer token to any URL passed to it. Meanwhile, `graph_url()` treats every string beginning with `http` as an already complete destination without validating its scheme or hostname. The OneDrive download operation obtains an `@microsoft.graph.dow ...[truncated 1620 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Restrict authenticated requests to `https://graph.microsoft.com/` or an explicit allowlist of documented Microsoft Graph hosts. - Reject plaintext HTTP and URLs containing unexpected credentials, ports, or malformed hostnames. - Use a separate function for pre-authenticated OneDrive download URLs that does not attach an OAuth header. - Validate redirect destinations, or disable automatic redirects and process each redirect after checking its scheme and hostname. - Avoid accepting arbitrary absolute URLs in `graph_url()`. Example separation: ```python from urllib.parse import urlparse def authorized_graph_request(method: str, url: str, **kwargs) -> requests.Response: parsed = urlparse(url) if parsed.scheme != "https" or parsed.hostname != "graph.microsoft.com": raise ValueError("Authenticated Graph requests must target graph.microsoft.com") return authorized_request(method, url, **kwargs) def download_preauthenticated(url: str) -> requests.Response: parsed = urlparse(url) if parsed.scheme != "https": raise ValueError("Download URL must use HTTPS") response = requests.get(url, timeout=60, allow_redirects=False) response.raise_for_status() return response ``` Microsoft's documented set of OneDrive storage hosts should be allowlisted if redirect handling is required. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/utils.py:20
Finding
Authentication always requests full-suite Microsoft 365 permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils.py:20-28` and `scripts/graph_auth.py:95-98` **Vulnerability Type**: Excessive OAuth permissions and violation of least privilege **Risk Level**: High ### Vulnerable Code ```python DEFAULT_SCOPES = [ "Mail.ReadWrite", "Mail.Send", "Calendars.ReadWrite", "Files.ReadWrite.All", "Contacts.ReadWrite", "offline_access", ] ``` ```python def command_device_login(args: argparse.Namespace) -> None: scopes = list(DEFAULT_SCOPES) client_id = args.client_id or DEFAULT_CLIENT_ID tenant_id = args.tenant_id or DEFAULT_TENANT ``` The Skill documentation additionally states that scope override is disabled and that the Skill always uses `DEFAULT_SCOPES`. ### Technical Analysis The device-code authentication flow always requests the complete permission set, even when a user needs only mail, calendar, contacts, or another isolated feature. The repository's own `docs/permission-profiles.md` defines narrower profiles, but the implementation does not allow those profiles to be selected during login. `offline_access` further increases the consequences because it permits acquisition of a refresh token. Combining persistent delegated access with unrelated read/write privileges creates a substantially broader compromise boundary than necessary for single-purpose deployments. This behavior exceeds the minimum privileges required for individual declared workflows. For example, a mail-only webhook deployment does not require OneDrive, calendar, or contacts write access. ### Attack Path 1. A user installs the Skill only to process mail notifications. 2. The device-login flow requests mail, mail sending, calendar, files, contacts, and offline access permissions. 3. The user consents because narrower runtime profiles cannot be selected. 4. An attacker later steals the token file, captures an access token, or compromises the worker process. 5. The attacker uses permissions unrelated ...[truncated 850 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Implement explicit permission profiles such as `mail-only`, `calendar-only`, `contacts-only`, `drive-only`, and `full-suite`. - Default to the narrowest profile appropriate for the command being configured. - Make sending permission optional for read-only mail deployments. - Prefer read-only scopes when mutation is not required. - Request incremental consent when the user first enables an additional feature. - Display the exact requested scopes and their consequences before beginning device login. - Store the selected profile in the authentication state and reject operations that require scopes not included in that profile. Example interface: ```bash python scripts/graph_auth.py device-login --profile mail-read python scripts/graph_auth.py device-login --profile mail-webhook python scripts/graph_auth.py device-login --profile calendar python scripts/graph_auth.py device-login --profile full-suite ``` A mail webhook profile should omit `Files.ReadWrite.All`, `Calendars.ReadWrite`, and `Contacts.ReadWrite`. `Mail.Send` should only be included when sending is explicitly enabled. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mail_webhook_adapter.py:101
Finding
Public webhook adapter buffers unbounded request bodies<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mail_webhook_adapter.py:101-119` **Vulnerability Type**: Unauthenticated denial of service through unbounded input processing **Risk Level**: Medium ### Vulnerable Code ```python def do_POST(self) -> None: if not self._validate_path(): self._send_json(404, {"error": "not_found"}) return if self._maybe_validation_handshake(): return length = int(self.headers.get("Content-Length", "0")) raw = self.rfile.read(length) if length else b"{}" try: payload = json.loads(raw.decode("utf-8")) except json.JSONDecodeError: self._send_json(400, {"error": "invalid_json"}) return events, rejected = parse_notification_events(payload, expected_client_state) accepted = enqueue_events(queue_file, events) self._send_json(202, {"accepted": accepted, "rejected": rejected}) ``` ### Technical Analysis The adapter is intended to receive traffic from the public Internet through Caddy. It parses the client-provided `Content-Length` value and reads that number of bytes into memory without a maximum size. Validation of `clientState` occurs only after the complete body has been read and decoded as JSON. Therefore, an unauthenticated client does not need to know the shared client state to consume memory, occupy a handler thread, or force expensive JSON parsing. Because the service uses `ThreadingHTTPServer`, concurrent large or slow requests can create multiple threads and amplify resource exhaustion. No application-level read timeout, concurrency bound, rate limit, or request-size limit is configured in the reviewed code. ### Attack Path 1. The attacker discovers the public `/graph/mail` endpoint. 2. The attacker opens many concurrent POST requests. 3. Each request declares a very large `Content-Length` or transmits a large JSON document slowly. 4. The threaded server allocates handlers and waits for or buffers each body. 5. Memory, CPU, f ...[truncated 645 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Define a conservative maximum request size suitable for Graph notification batches. - Return HTTP `413 Payload Too Large` before reading a body that exceeds the limit. - Reject invalid, negative, missing, or non-numeric `Content-Length` values as appropriate. - Configure socket read and header timeouts. - Bound concurrent request processing rather than creating unrestricted threads. - Configure Caddy request-body limits and rate limiting. - Apply network-level rate limits where possible. - Monitor rejection rates and resource utilization. Example application-level control: ```python MAX_BODY_BYTES = 256 * 1024 raw_length = self.headers.get("Content-Length") if raw_length is None: self._send_json(411, {"error": "length_required"}) return try: length = int(raw_length) except ValueError: self._send_json(400, {"error": "invalid_content_length"}) return if length < 0 or length > MAX_BODY_BYTES: self._send_json(413, {"error": "payload_too_large"}) return raw = self.rfile.read(length) ``` The reverse proxy should enforce an equal or smaller limit so oversized traffic is rejected before reaching Python. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mail_webhook_adapter.py:125
Finding
Webhook authentication secrets are exposed through logs and process arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mail_webhook_adapter.py:125-130`, `scripts/setup_mail_webhook_ec2.sh:202-208`, and `scripts/run_mail_webhook_e2e_setup.sh:514-520` **Vulnerability Type**: Sensitive information exposure **Risk Level**: Medium ### Vulnerable Code ```python def serve(args: argparse.Namespace) -> None: client_state = args.client_state or secrets.token_urlsafe(24) queue_file = Path(args.queue_file).expanduser().resolve() print("Starting Graph mail webhook adapter") print(f"- listen: http://{args.host}:{args.port}{args.path}") print(f"- queue: {queue_file}") print(f"- clientState: {client_state}") print("Use this same clientState when creating subscriptions.") ``` ```ini [Service] Type=simple EnvironmentFile=$ENV_FILE WorkingDirectory=$REPO_ROOT ExecStart=$PYTHON_BIN $WORKER_SCRIPT loop --session-key \${OPENCLAW_SESSION_KEY} --hook-url \${OPENCLAW_HOOK_URL} --hook-token \${OPENCLAW_HOOK_TOKEN} Restart=always RestartSec=3 ``` ```bash echo "Setup and validation completed. Summary:" echo "- Domain: https://$DOMAIN$ADAPTER_PATH" echo "- Client state: $CLIENT_STATE" echo "- Subscription ID: $SUBSCRIPTION_ID" echo "- Env file: $ENV_FILE" ``` ### Technical Analysis The Graph webhook `clientState` is treated as a secret elsewhere in the project, but the adapter prints it during startup. Under systemd, standard output normally enters the journal, creating a persistent copy available to users or services with journal-reading privileges. The end-to-end setup script also prints the same value to its terminal output, where it may be captured by automation logs or screenshots. The worker's OpenClaw hook token is expanded into the `ExecStart` command line. Depending on operating-system process visibility and systemd tooling, command arguments can be exposed through process inspection, service status output, diagnostics, or monitoring agents. Although `/etc/default/graph-mail-webhook` is set to mode `0 ...[truncated 1594 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove all output that prints the full client-state value. - Display only a non-sensitive confirmation or a short fingerprint when diagnostics are necessary. - Do not pass the hook token or client state as command-line arguments. - Have the Python worker and adapter read secrets directly from protected environment variables or credential files. - Consider systemd credential facilities such as `LoadCredential=` where supported. - Review journal access and prevent unnecessary users or services from reading service logs. - Ensure setup automation redacts secrets from CI logs, shell tracing, and support bundles. - Rotate both secrets after any suspected exposure. For example, use a service command without secret arguments: ```ini [Service] EnvironmentFile=/etc/default/graph-mail-webhook ExecStart=/usr/bin/python3 /path/scripts/mail_webhook_worker.py loop ``` The worker should then read `OPENCLAW_HOOK_TOKEN`, `OPENCLAW_HOOK_URL`, and related values internally with `os.environ`. The adapter should log only: ```python print("- clientState: configured") ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (79)

External Script Fetching

High
Category
Supply Chain
Content
- Dedicated OpenClaw hook token for webhook auth
- Graph `clientState` validation for notification integrity
- Local queue + dedupe before agent wake-up
- No dependency on `curl | bash` installers in project scripts

## Supported release line
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The description presents a broad Microsoft 365 integration centered on webhook wake signals and managing multiple resource types (mail, calendar, OneDrive, contacts). This code chunk does not implement webhook handling or any Outlook/calendar/contacts behavior. Its actual purpose is much narrower: direct OneDrive file management through Microsoft Graph. While OneDrive is part of the declared scope, the primary declared emphasis on webhook-based wake signals and inbox polling is not reflected here, and the implemented sharing-link capability is an undeclared OneDrive-specific action. Therefore the description does not accurately represent this code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The supplied code only implements Outlook mail access through Microsoft Graph: listing messages, fetching a single message, optionally including body content, and modifying message state by marking as read or moving to a folder. The declared description emphasizes a broader Microsoft 365 Graph skill with webhook-based wake signals and management of mail, calendar, OneDrive, and contacts. No webhook or wake-signal behavior appears in this chunk, and no calendar, OneDrive, or contacts handling is present. Additionally, the code includes message mutation capabilities (mark-read and move), which go beyond simple inbox polling reduction. While this chunk may be one component of a larger skill, taken on its own it does not accurately represent the declared broader purpose.

Context Leakage

High
Category
Data Exfiltration
Content
```

- `saveToSentItems` is `True` by default. Use `--no-save-copy` to disable.
- Attachments are sent as `fileAttachment` and are limited on this endpoint; for large files, implement upload session flow.

## Useful folder IDs
Confidence
85% confidence
Finding
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.

Context Leakage

High
Category
Data Exfiltration
Content
```

- `saveToSentItems` is `True` by default. Use `--no-save-copy` to disable.
- Attachments are sent as `fileAttachment` and are limited on this endpoint; for large files, implement upload session flow.

## Useful folder IDs
Confidence
85% confidence
Finding
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.

Ssd 3

High
Confidence
99% confidence
Finding
Displaying the full OPENCLAW_SESSION_KEY is a direct secret disclosure issue. In a diagnostic script, this is especially risky because operators often paste outputs into tickets, chats, or monitoring systems, turning a local secret into a broadly distributed credential leak.

Missing User Warnings

High
Confidence
92% confidence
Finding
Creating a sharing link changes access control on a remote file and can expose data beyond the intended audience, especially if anonymous links are allowed. In an agent skill context, this is more dangerous because an LLM or automation layer could trigger external sharing as a side effect without a clear human confirmation step.

Credential Access

High
Category
Privilege Escalation
Content
echo "[1/7] Installing dependencies..."
run_cmd apt-get update -y
run_cmd apt-get install -y python3 python3-pip curl gnupg debian-keyring debian-archive-keyring apt-transport-https
ok "Dependencies installed"

if [[ "$DRY_RUN" == "true" ]] || ! command -v caddy >/dev/null 2>&1; then
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
if [[ "$DRY_RUN" == "true" ]]; then
    info "[DRY-RUN] install Caddy and apt repo configuration"
  else
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
    curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list >/dev/null
    apt-get update -y
    apt-get install -y caddy
Confidence
88% confidence
Finding
The script downloads a third-party repository GPG key and apt source definition over the network and immediately trusts them as root, then installs packages from that repository. If the source is compromised, redirected, or replaced, this can lead to arbitrary package installation and full system compromise on the EC2 host.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises extensive capabilities including environment access, file read/write, network access, and shell execution, but does not declare any explicit tool scope or permissions boundary. In an agent ecosystem, this weakens reviewability and least-privilege enforcement, making it easier for a broad-privilege skill to be installed or invoked without operators understanding its effective access.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- `state/mail_webhook_dedupe.json`
- **Automated EC2 bootstrap** (Caddy + systemd + renew timer):
  ```bash
  sudo bash scripts/setup_mail_webhook_ec2.sh \
    --domain graphhook.example.com \
    --hook-url http://127.0.0.1:18789/hooks/wake \
    --hook-token "<OPENCLAW_HOOK_TOKEN>" \
Confidence
90% confidence
Finding
The skill instructs users to run a repository-provided shell script with sudo, which grants the script full root privileges to modify system configuration and services. Even though the document warns that these setup scripts are privileged and recommends dry-run/manual review, executing untrusted or insufficiently reviewed repo scripts as root creates a meaningful privilege-escalation and host-compromise risk.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- Use `--dry-run` to preview all privileged writes and service actions before applying changes.
- **One-command setup (steps 2..6)**:
  ```bash
  sudo bash scripts/run_mail_webhook_e2e_setup.sh \
    --domain graphhook.example.com \
    --hook-token "<OPENCLAW_HOOK_TOKEN>" \
    --hook-url "http://127.0.0.1:18789/hooks/wake" \
Confidence
90% confidence
Finding
This one-command setup flow uses sudo for a script that can write to /etc, systemd units, and potentially alter running services, giving the repository code root-level control over the host. The context makes this somewhat less suspicious because the privilege need is openly disclosed and related to system setup, but it remains dangerous if the script is malicious, compromised, or insufficiently reviewed.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- Output ends with `READY_FOR_PUSH: YES` when setup is fully validated.
- **Include OpenClaw hook config in automation**:
  ```bash
  sudo bash scripts/run_mail_webhook_e2e_setup.sh \
    --domain graphhook.example.com \
    --hook-token "<OPENCLAW_HOOK_TOKEN>" \
    --configure-openclaw-hooks \
Confidence
93% confidence
Finding
This sudo-invoked automation can also patch OpenClaw configuration and restart OpenClaw services, expanding impact beyond generic system setup into modification of agent runtime behavior. If abused or compromised, it could alter hook configuration, redirect traffic, weaken authentication, or persist malicious changes via service definitions, making the privilege boundary especially sensitive.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```
- **Minimal-input smoke tests**:
  ```bash
  sudo bash scripts/run_mail_webhook_smoke_tests.sh \
    --domain graphhook.example.com \
    --create-subscription \
    --test-email tar.alitar@outlook.com
Confidence
82% confidence
Finding
Even the smoke-test path is documented as a sudo shell invocation, which normalizes elevated execution for repository automation and increases the chance users will run complex scripts as root without adequate review. The stated purpose is operational testing rather than stealthy behavior, but the trust model is still risky because root execution of testing code can modify system state or be extended to do so.

Session Persistence

Medium
Category
Rogue Agent
Content
### 4. Run one setup command

This script installs Caddy and systemd units (adapter/worker/timer), creates the Graph subscription, and persists runtime values.

```bash
sudo bash scripts/run_mail_webhook_e2e_setup.sh \
Confidence
80% confidence
Finding
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.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1) Verify active subscription resource (`me/messages` recommended).
2) Verify notification URL is public HTTPS and reachable.
3) Check adapter service and reverse proxy status.
4) Run `sudo bash scripts/diagnose_mail_webhook_e2e.sh --domain <your-domain> --repo-root "$(pwd)"` for full pipeline checks.

## Subscription has `clientState: null`
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1) Verify active subscription resource (`me/messages` recommended).
2) Verify notification URL is public HTTPS and reachable.
3) Check adapter service and reverse proxy status.
4) Run `sudo bash scripts/diagnose_mail_webhook_e2e.sh --domain <your-domain> --repo-root "$(pwd)"` for full pipeline checks.

## Subscription has `clientState: null`
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1) Verify active subscription resource (`me/messages` recommended).
2) Verify notification URL is public HTTPS and reachable.
3) Check adapter service and reverse proxy status.
4) Run `sudo bash scripts/diagnose_mail_webhook_e2e.sh --domain <your-domain> --repo-root "$(pwd)"` for full pipeline checks.

## Subscription has `clientState: null`
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1) Verify active subscription resource (`me/messages` recommended).
2) Verify notification URL is public HTTPS and reachable.
3) Check adapter service and reverse proxy status.
4) Run `sudo bash scripts/diagnose_mail_webhook_e2e.sh --domain <your-domain> --repo-root "$(pwd)"` for full pipeline checks.

## Subscription has `clientState: null`
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1) Verify active subscription resource (`me/messages` recommended).
2) Verify notification URL is public HTTPS and reachable.
3) Check adapter service and reverse proxy status.
4) Run `sudo bash scripts/diagnose_mail_webhook_e2e.sh --domain <your-domain> --repo-root "$(pwd)"` for full pipeline checks.

## Subscription has `clientState: null`
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1) Verify active subscription resource (`me/messages` recommended).
2) Verify notification URL is public HTTPS and reachable.
3) Check adapter service and reverse proxy status.
4) Run `sudo bash scripts/diagnose_mail_webhook_e2e.sh --domain <your-domain> --repo-root "$(pwd)"` for full pipeline checks.

## Subscription has `clientState: null`
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1) Verify active subscription resource (`me/messages` recommended).
2) Verify notification URL is public HTTPS and reachable.
3) Check adapter service and reverse proxy status.
4) Run `sudo bash scripts/diagnose_mail_webhook_e2e.sh --domain <your-domain> --repo-root "$(pwd)"` for full pipeline checks.

## Subscription has `clientState: null`
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1) Verify active subscription resource (`me/messages` recommended).
2) Verify notification URL is public HTTPS and reachable.
3) Check adapter service and reverse proxy status.
4) Run `sudo bash scripts/diagnose_mail_webhook_e2e.sh --domain <your-domain> --repo-root "$(pwd)"` for full pipeline checks.

## Subscription has `clientState: null`
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1) Verify active subscription resource (`me/messages` recommended).
2) Verify notification URL is public HTTPS and reachable.
3) Check adapter service and reverse proxy status.
4) Run `sudo bash scripts/diagnose_mail_webhook_e2e.sh --domain <your-domain> --repo-root "$(pwd)"` for full pipeline checks.

## Subscription has `clientState: null`
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1) Verify active subscription resource (`me/messages` recommended).
2) Verify notification URL is public HTTPS and reachable.
3) Check adapter service and reverse proxy status.
4) Run `sudo bash scripts/diagnose_mail_webhook_e2e.sh --domain <your-domain> --repo-root "$(pwd)"` for full pipeline checks.

## Subscription has `clientState: null`
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Static analysis

No suspicious patterns detected.