Back to skill

Security audit

Meeting Cadence Optimizer

Security checks across malware telemetry and agentic risk

Overview

The meeting analysis itself is mostly clear, but the package also includes under-disclosed tools that can access and modify unrelated Fulcra and Attio CRM data.

Review this before installing. The core cadence report and optional Fulcra save are understandable, but the package also ships extra CRM, health/metric, and generic deletion/update utilities that are not necessary for the stated task. Only use it in an environment where you trust the publisher and the relevant environment variables, and avoid providing Attio credentials unless you intentionally want those unrelated CRM tools available.

SkillSpector

By NVIDIA
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (24)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
candidates = [[*shlex.split(command), "auth", "print-access-token"]]
    for cmd in candidates:
        try:
            token = subprocess.check_output(
                cmd,
                env=env,
                text=True,
Confidence
88% confidence
Finding
token = subprocess.check_output( cmd, env=env, text=True, stderr=subprocess.DEVNULL, timeout=45,

Tainted flow: 'req' from os.environ.get (line 52, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers["Content-Type"] = "application/json"
    req = urllib.request.Request(url, data=data, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            body = resp.read().decode() or "{}"
            return resp.status, json.loads(body)
    except urllib.error.HTTPError as exc:
Confidence
95% confidence
Finding
with urllib.request.urlopen(req, timeout=30) as resp:

Tainted flow: 'cmd' from os.environ.get (line 32, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
env.setdefault("PYTHONUTF8", "1")
    cmd = [*shlex.split(CLI_COMMAND, posix=(os.name != "nt")), *args]
    try:
        proc = subprocess.run(cmd, env=env, text=True, capture_output=True, timeout=120)
    except (FileNotFoundError, subprocess.TimeoutExpired) as exc:
        return 1, f"CLI invocation failed: {exc}"
    if proc.returncode != 0:
Confidence
95% confidence
Finding
proc = subprocess.run(cmd, env=env, text=True, capture_output=True, timeout=120)

Tainted flow: 'req' from os.environ.get (line 82, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers["Content-Type"] = "application/json"
    req = urllib.request.Request(API_BASE + path, data=data, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req, timeout=30) as response:
            body = response.read()
            return response.status, body.decode() if body else ""
    except urllib.error.HTTPError as exc:
Confidence
95% confidence
Finding
with urllib.request.urlopen(req, timeout=30) as response:

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill advertises read-oriented analysis but exposes shell, network, environment, and file-write capabilities without any declared permissions boundary. Even if the documented analyze path is intended to be read-only, the presence of undeclared write and network-capable execution increases the chance of unintended data modification, credential exposure, or broader system access when the skill is invoked or extended.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill description says it reads existing Fulcra annotations deterministically, but the documented save path writes a new annotation and may create supporting definitions/tags. This mismatch can cause an agent or user to invoke a seemingly analytical skill that performs persistent remote writes, and the inaccurate claim about using morning check-in history undermines trust and informed consent about what data is actually processed.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The skill metadata emphasizes a read-back, deterministic analysis workflow, but the save path persists a new "Cadence Analysis" annotation. That creates an integrity and scope-expansion issue: a caller expecting read-only behavior may unknowingly cause writes to the user's data store, which can surprise users, create unnecessary retention of derived personal analytics, and violate least-privilege expectations.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
This module is a general-purpose Attio CRM client with capabilities to list objects, query records, create, update, delete, and add notes. That scope materially exceeds the stated purpose of a meeting-cadence optimizer that should read Fulcra annotations deterministically and not perform broad third-party CRM operations, creating an unnecessary data access and exfiltration surface. The mismatch between declared function and actual capability makes accidental or abusive use much more likely.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The file exposes record-creation, upsert, update, note-writing, and deletion operations against Attio, including destructive deletion of records. Those mutation capabilities are unjustified for a skill whose documented role is analytics over existing user debrief/check-in history, and they create a clear path to integrity loss, unwanted CRM modification, or destructive actions if invoked by mistake or abuse. Because these are built into both library and CLI flows, the attack surface is broader than a dormant helper.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The client can enumerate Attio objects and query arbitrary people and companies, which exceeds the skill's described scope of using Fulcra annotations for meeting-load analysis. Even if limited to reads, this broadens access to unrelated CRM data and increases privacy risk, data minimization violations, and the chance of cross-context data misuse. The disconnect from the skill's stated purpose makes the capability harder to justify as benign necessity.

Intent-Code Divergence

Medium
Confidence
84% confidence
Finding
The docstring presents dry-run support as a safety property, but writes and deletes execute normally unless the caller explicitly sets dry_run. That can mislead maintainers or downstream skills into assuming operations are non-destructive by default, increasing the risk of unintended CRM modification or deletion. In a skill that should not be mutating CRM data at all, misleading safety messaging makes the mismatch more dangerous.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
This shared helper exposes raw calendar access plus sleep and generic metric retrieval even though the meeting-cadence skill is described as relying on debrief and check-in history and explicitly says not to use raw calendar data alone or objective health pulls. That creates a scope-expansion/privacy risk: code imported for a narrow purpose can access significantly broader personal data than users and reviewers would expect.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
metric_samples allows retrieval of arbitrary named health metrics over arbitrary time windows, which is far broader than needed for a meeting-cadence optimizer. In this skill context that unjustified access is especially concerning because it enables opportunistic collection of sensitive biometric or health data unrelated to the user-facing purpose.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
This helper exposes create, update, delete, and record operations for arbitrary annotations, while the skill is described as a read-only meeting-cadence analyzer. That capability mismatch is dangerous because a prompt-influenced agent or repurposed wrapper could mutate or destroy user data far beyond the declared scope.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
resolve_tags() can create missing tags automatically via create_tag(), which grants this helper an administrative write path unrelated to meeting-load analysis. In the context of a narrowly scoped analytics skill, this expands authority unnecessarily and can lead to unauthorized metadata creation or taxonomy pollution.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
def run_cli(args: list[str]) -> tuple[int, str]:
    env = os.environ.copy()
    env.setdefault("PYTHONUTF8", "1")
    cmd = [*shlex.split(CLI_COMMAND, posix=(os.name != "nt")), *args]
    try:
Confidence
60% confidence
Finding
os.environ.copy()

Env Variable Harvesting

High
Category
Data Exfiltration
Content
def access_token() -> str:
    env_token = os.environ.get("FULCRA_ACCESS_TOKEN")
    if env_token:
        return env_token.strip()
Confidence
70% confidence
Finding
os.environ.get("FULCRA_ACCESS_TOKEN

Env Variable Harvesting

High
Category
Data Exfiltration
Content
if env_token:
        return env_token.strip()

    env = os.environ.copy()
    env["HOME"] = DEFAULT_HOME

    command = os.environ.get("FULCRA_CLI_COMMAND", "uv tool run fulcra-api")
Confidence
60% confidence
Finding
os.environ.copy()

Unvalidated Output Injection

High
Category
Output Handling
Content
env.setdefault("PYTHONUTF8", "1")
    cmd = [*shlex.split(CLI_COMMAND, posix=(os.name != "nt")), *args]
    try:
        proc = subprocess.run(cmd, env=env, text=True, capture_output=True, timeout=120)
    except (FileNotFoundError, subprocess.TimeoutExpired) as exc:
        return 1, f"CLI invocation failed: {exc}"
    if proc.returncode != 0:
Confidence
95% confidence
Finding
subprocess.run(cmd, env=env, text=True, capture_output

Credential Access

High
Category
Privilege Escalation
Content
stdlib-only (urllib) so it runs under any `uv run`/python without extra installs,
mirroring the Fulcra annotation helper. It is BOTH a library (import the functions)
and a CLI (skills shell out to it). Every write supports --dry-run, and the token is
loaded via concierge_secrets (env ATTIO_API_KEY or ~/.fulcra-concierge/secrets.json) and
never printed.

Attio shapes used (verify against the live API; envelopes are the documented ones):
Confidence
70% confidence
Finding
secrets.json

Credential Access

High
Category
Privilege Escalation
Content
2. ~/.fulcra-concierge/secrets.json -- the persistent local store.

Secrets are never printed. Skills read keys through `get_secret`; they should
not open secrets.json themselves. Keeping one loader means one place to harden
(file perms, future keychain support) instead of N copies across skills.
"""
from __future__ import annotations
Confidence
70% confidence
Finding
secrets.json

Credential Access

High
Category
Privilege Escalation
Content
import os
from pathlib import Path

SECRETS_PATH = Path(os.environ.get("FULCRA_CONCIERGE_HOME", Path.home() / ".fulcra-concierge")) / "secrets.json"


def _load_file() -> dict:
Confidence
70% confidence
Finding
secrets.json

Credential Access

High
Category
Privilege Escalation
Content
def set_secret(name: str, value: str) -> None:
    """Persist a secret into secrets.json (merging with any existing keys) and
    lock the file down to the current user. Used by setup helpers, not skills."""
    SECRETS_PATH.parent.mkdir(parents=True, exist_ok=True)
    data = _load_file()
Confidence
70% confidence
Finding
secrets.json

Credential Access

High
Category
Privilege Escalation
Content
Secrets are never printed. Skills read keys through `get_secret`; they should
not open secrets.json themselves. Keeping one loader means one place to harden
(file perms, future keychain support) instead of N copies across skills.
"""
from __future__ import annotations
Confidence
70% confidence
Finding
keychain

VirusTotal

60/60 vendors flagged this skill as clean.

View on VirusTotal

Static analysis

No suspicious patterns detected.