Back to skill

Security audit

gogcli-mcp-calendar

Security checks for vulnerabilities and agentic risk

Overview

This is mostly a real Google Calendar/Meet integration, but it exposes high-impact account actions with some under-disclosed scope and one destructive Meet action that lacks an enforced confirmation step.

Review this before installing if the MCP host can act without your explicit approval. Use a dedicated Google account or set GOG_READONLY=1 for read-only use, avoid configuring Zoom credentials unless needed, and treat gog_meet_end, calendar deletion, unsubscribe, and escape-hatch run tools as high-impact account operations.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
src/tools/calendar-extra.ts:75
Finding

Google Meet Conference Termination Bypasses User Confirmation

Content
View full analysis

Vulnerability Details

File Location: src/tools/calendar-extra.ts:75-86
Vulnerability Type: Unconfirmed destructive operation
Risk Level: Medium

Vulnerable Code

ts
server.registerTool('gog_meet_end', {
  description: 'End the active conference in a Google Meet space.',
  annotations: { destructiveHint: true },
  inputSchema: z.object({
    meetingCode: z.string().describe('Meeting code'),
    account: accountParam,
  }),
}, async ({ meetingCode, account }) => {
  return runOrDiagnose(
    ['meet', 'end', pos(meetingCode), '--force'],
    { account },
  ); // gog gates this op; without --force the runner's --no-input makes it refuse
});

The same behavior is present in the shipped runtime bundle at dist/index.js:36838-36849.

Technical Analysis

The gog_meet_end MCP tool performs an immediate destructive operation against the user's authenticated Google account. Its handler unconditionally passes --force, explicitly bypassing gogcli's interactive safety gate.

The destructiveHint: true annotation only informs the MCP host about the operation's nature; it does not technically require user approval. Unlike other destructive operations in this file, the handler does not call requireDispatchConfirmation, accept a confirmToken, or refuse execution when the host cannot elicit confirmation.

The meeting code is protected from command-line option injection by pos(meetingCode), and the runner uses argument-array process spawning rather than a shell. The confirmed issue is therefore not command injection, but the absence of an enforced authorization checkpoint before a destructive action.

Attack Path

  1. The Skill runs with access to a Google account authenticated through gogcli.
  2. An MCP caller invokes gog_meet_end and supplies the target meeting code and account.
  3. The handler constructs meet end <meetingCode> --force.
  4. runOrDiagnose dispatches the command without requesting or validating user confirmat ...[truncated 976 chars]
Remediation
View remediation

Remediation Suggestions

  1. Invoke requireDispatchConfirmation before calling runOrDiagnose, showing the meeting code, selected account, and the fact that all participants will be disconnected.
  2. Add confirmToken: confirmTokenParam to the input schema and implement the existing confirmation-token fallback for MCP hosts that do not support interactive elicitation.
  3. Refuse the operation when confirmation cannot be obtained rather than silently forcing execution.
  4. Append --force only after successful interactive confirmation or validation of a confirmation token bound to the exact account and meeting code.
  5. Add tests covering acceptance, rejection, unsupported elicitation, stale or mismatched confirmation tokens, and verification that runOrDiagnose is never called after refusal.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (59)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This code chunk appears to be bundled third-party/library infrastructure (Zod core and locale definitions), focused on validating and transforming JavaScript data structures. It contains no evidence of calendar or meeting operations, no calls to Google services, no network/API handling for Calendar or Meet, and no user-action logic matching the declared purpose. Because the actual behavior shown is materially unrelated to the declared skill purpose, this is a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The code shown is unrelated to calendar or meeting management. It consists of localized error-formatting functions for validation issues (e.g., invalid type, invalid format, too big/small, unrecognized keys) across many languages. There are no signs of Google API calls, calendar event CRUD operations, Meet space management, invitation responses, participant listing, or call history retrieval. This is a materially different primary purpose from the declared description, so it is a clear mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description says this skill manages Google Calendar and Google Meet operations. However, the supplied code does not interact with Google services, external APIs, calendars, meetings, participants, or call history. Instead, it defines localized validation/error formatting functions from the Zod library for multiple languages. This is a materially different primary purpose and lacks any evidence of the declared capabilities. Therefore, the description does not accurately represent the actual code behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The code shown contains multilingual Zod locale strings and core schema compilation/validation logic. There is no evidence of Google Calendar or Google Meet API calls, event/meeting management, scheduling, invitation handling, participant listing, or conference control. Its primary purpose is unrelated input validation infrastructure, which materially differs from the declared calendar/meeting-management functionality.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The code shown is clearly library/framework code for defining data schemas and converting them to/from JSON Schema (e.g., Zod types like string, number, object, union, record, file, transform, toJSONSchema processors). There are no functions for listing, creating, updating, or deleting calendar events; no invitation responses; no Meet space creation or conference termination; and no participant/call-history retrieval. This is not supporting implementation for a calendar skill—it is an unrelated validation library. Therefore the declared description does not accurately represent the actual behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The supplied code chunk appears to be bundled library/runtime code for schema processing and protocol validation, not business logic for Google Calendar or Google Meet actions. There are no visible calls to Google APIs, no calendar/meet-specific endpoints, no event CRUD behavior, and no conference or participant management. This is a material description-to-behavior mismatch because the declared primary purpose is Google Calendar/Meet management, while the actual code shown serves a generic validation/protocol role.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This code is infrastructure/protocol code, not application logic for Google Calendar or Google Meet. It declares schemas for content blocks, tools, prompts, resources, notifications, requests/results, protocol errors, input-required flows, and a generic Protocol class for JSON-RPC-style messaging. There are no Google-specific endpoints, OAuth scopes for Calendar/Meet use, event objects, meeting-space operations, or any concrete scheduling/meeting management functions. Because the actual code’s primary purpose is materially different from the declared purpose, this is a clear mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The supplied code appears to be framework/runtime support code rather than an integration with Google Calendar or Google Meet. There are no visible calls to Google APIs, no calendar event operations, no Meet space/conference management, no participant/history retrieval, and no OAuth/resource access specific to Google services. This is a material description-behavior mismatch because the declared primary purpose is domain-specific calendar/meeting management, while the actual code shown is unrelated protocol and validation infrastructure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Most of the code aligns with Google Calendar and Google Meet management. However, the declared description does not mention Zoom at all, while the code includes tools to store and validate Zoom OAuth credentials. That is an undeclared capability involving a different external service. Additionally, the description is framed around calendar events and Meet spaces, but the code also performs broader calendar-administration tasks such as deleting calendars, unsubscribing calendars, and listing ACL/sharing rules. Those are adjacent but materially broader than event/Meet management as described. Therefore this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Most of the tested behavior aligns broadly with Google Calendar/Meet management, especially Meet creation/end/history/participants and various calendar operations. However, the code clearly exposes undeclared capabilities outside the stated scope. The largest mismatch is Zoom OAuth setup/doctor, which is not Google Calendar or Google Meet and introduces another service entirely. In addition, the tools include broader calendar administration and subscription/deletion actions, not just event management. These are material extra capabilities rather than mere implementation details, so this should be flagged as a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared purpose is centered on managing Google Calendar events and Google Meet spaces. The actual code is only a test runner configuration (vitest.config.ts) that configures environment variables and coverage settings for tests. It contains no logic for scheduling, listing, creating, updating, or deleting calendar events; no Meet space or conference handling; and no interaction with Google APIs. This is a materially different primary purpose, so the description does not accurately represent the code chunk.

Content

No source excerpt is available for this finding.

YARA rule 'agent_skill_prompt_injection_hidden_instructions': Prompt injection or hidden instructions embedded in AI agent skill text [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · dist/index.js (reported line 43)May include surrounding context.

js
../node_modules/@chrischall/mcp-utils/dist/caller/index.js
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
var storage2 = new AsyncLocalStorage2();
function withCallerCapabilities(capabilities, fn) {
  return capabilities ? storage2.run(capabilities, fn) : fn();
}
function currentCallerCapabilities() {
  return storage2.getStore();
}
var ENVELOPE_CAPABILITIES_KEY = "io.modelcontextprotocol/clientCapabilities";
var ELICITATION_MODES = ["form", "url"];
function isRecord(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function callerCapabilities(ctx) {
  const envelope = isRecord(ctx) && isRecord(ctx.mcpReq) ? ctx.mcpReq.envelope : void 0;
  if (isRecord(envelope)) {
    const declared = envelope[ENVELOPE_CAPABILITIES_KEY];
    if (isRecord(declared))
      return declared;
  }
  return currentCallerCapabilities();
}
function callerAcceptsFormElicitation(ctx) {
  const capabilities = callerCapabilities(ctx);
  if (!capa

YARA rule 'exploit_framework': Exploit framework components and payloads [hacktools]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · dist/index.js (reported line 18139)May include surrounding context.

js
ams);
}
function keyof(schema) {
  const shape = schema._zod.def.shape;
  return _enum2(Object.keys(shape));
}
var ZodObject = /* @__PURE__ */ $constructor("ZodObject", (inst, def) => {
  _ensureDefaultMemoizer();
  $ZodObjectJIT.init(inst, def);
  ZodType.init(inst, def);
  inst._zod.processJSONSchema = (ctx, json2, params) => objectProcessor(inst, ctx, json2, params);
  util_exports.installLazyProp(inst, "shape", (self) => self._zod.def.shape, false);
}, {
  keyof() {
    return _enum2(Object.keys(this._zod.def.shape));
  },
  catchall(catchall) {
    return this.clone(util_exports.mergeDefs(this._zod.def, { catchall }));
  },
  passthrough() {
    return this.clone(util_exports.mergeDefs(this._zod.def, { catchall: unknown() }));
  },
  loose() {
    return this.clone(util_exports.mergeDefs(this._zod.def, { catchall: unknown() }));
  },
  strict() {
    return this.clone(util_exports.mergeDefs(this._zod.def, { catchall: never() }));
  },
  strip() {
    return this.clone(util_exports

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · dist/index.js (reported line 27070)May include surrounding context.

js
function validateAsync3() {
      const ruleErrs = gen.let("ruleErrs", null);
      gen.try(() => assignValid((0, codegen_1._)`await `), (e) => gen.assign(valid, false).if((0, codegen_1._)`${e} instanceof ${it.ValidationError}`, () => gen.assign(ruleErrs, (0, codegen_1._)`${e}.errors`), () => gen.throw(e)));
      return ruleErrs;
    }
    function validateSync() {
      const validateErrs = (0, codegen_1._)`${validateRef}.errors`;

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · dist/index.js (reported line 33463)May include surrounding context.

js
if (typeof promptName !== "string") return;
        const prompt = this._registeredPrompts[promptName];
        if (prompt === void 0 || !prompt.enabled) return;
        return prompt.scopeChallenge?.(context);
      }
      default:
        return;

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · dist/index.js (reported line 33664)May include surrounding context.

js
if (typeof promptName !== "string") return;
        const prompt = this._registeredPrompts[promptName];
        if (prompt === void 0 || !prompt.enabled) return;
        return prompt.scopeChallenge?.(context);
      }
      default:
        return;

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · dist/index.js (reported line 34578)May include surrounding context.

js
return walk(value, keep, drop);
}
function normalizeRules(rules) {
  return rules.map((rule) => typeof rule === "string" ? rule.toLowerCase() : new RegExp(rule.source, rule.flags));
}
function matchesRule(key, rules) {
  const lower = key.toLowerCase();

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · dist/index.js (reported line 35533)May include surrounding context.

js
var TOKEN_LEFT_BOUNDARY = "(?<![A-Za-z0-9+/])";
var GOOGLE_TOKEN_PATTERNS = [
  new RegExp(`${TOKEN_LEFT_BOUNDARY}ya29\\.[A-Za-z0-9._\\-]+`, "g"),
  // OAuth2 access tokens
  new RegExp(`${TOKEN_LEFT_BOUNDARY}1//[A-Za-z0-9._\\-]+`, "g")
  // OAuth2 refresh tokens
];

Credential Access

High
Category
Privilege Escalation
Confidence
82% confidence
Finding

The skill can store Zoom Server-to-Server OAuth credentials in gogcli's keyring even though the skill is presented as a Google Calendar/Meet integration. Hidden credential-ingestion capability increases risk because users and orchestrators may not realize the skill can onboard and retain third-party secrets.

Content

Scanner excerpt · dist/index.js (reported line 36866)May include surrounding context.

js
return runOrDiagnose(args, { account });
  });
  server.registerTool("gog_zoom_auth_setup", {
    description: "Store Zoom Server-to-Server (S2S) OAuth credentials so calendar events can be attached to Zoom meetings via the --with-zoom flag on gog_calendar_create / gog_calendar_update. Credentials are saved in gogcli's keyring under the given alias.",
    annotations: { destructiveHint: true },
    inputSchema: external_exports.object({
      accountId: external_exports.string().describe("Zoom S2S OAuth account ID"),

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · mint.yaml (reported line 51)May include surrounding context.

yaml
The Google account to act as. Required for the startup auth bootstrap
      (the refresh token is imported under this email); otherwise defaults to
      gog's single/most recent account.
  - name: GOG_KEYRING_BACKEND
    required: false
    help: >-
      gog's credential store. Set to "file" on a headless host — there is no OS

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · mint.yaml (reported line 56)May include surrounding context.

yaml
The Google account to act as. Required for the startup auth bootstrap
      (the refresh token is imported under this email); otherwise defaults to
      gog's single/most recent account.
  - name: GOG_KEYRING_BACKEND
    required: false
    help: >-
      gog's credential store. Set to "file" on a headless host — there is no OS

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · mint.yaml (reported line 61)May include surrounding context.

yaml
The Google account to act as. Required for the startup auth bootstrap
      (the refresh token is imported under this email); otherwise defaults to
      gog's single/most recent account.
  - name: GOG_KEYRING_BACKEND
    required: false
    help: >-
      gog's credential store. Set to "file" on a headless host — there is no OS

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · mint.yaml (reported line 55)May include surrounding context.

yaml
required: false
    help: >-
      gog's credential store. Set to "file" on a headless host — there is no OS
      keychain — so the keyring lives on the persistent data dir.
  - name: GOG_KEYRING_PASSWORD
    secret: true
    required: false

Credential Access

High
Category
Privilege Escalation
Confidence
71% confidence
Finding

This line explicitly states that GOG_KEYRING_PASSWORD is deliberately not stripped from the spawned CLI environment. Even though the intent is operationally necessary, retaining a decryption secret in child-process environment variables increases exposure through process inspection, crash dumps, debugging tools, or accidental subprocess propagation.

Content

Scanner excerpt · mint.yaml (reported line 60)May include surrounding context.

yaml
secret: true
    required: false
    help: >-
      Encrypts gog's file keyring on the data dir. Required with
      GOG_KEYRING_BACKEND=file. Deliberately not stripped from the spawned
      CLI's environment — it is the one credential gog itself reads.
  - name: GOG_READONLY

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · dist/index.js (reported line 36181)May include surrounding context.

js
import { registerExtraCalendarTools } from './tools/calendar-extra.js';


// Seed gog's keyring from GOG_CLIENT_ID/SECRET/REFRESH_TOKEN/ACCOUNT when the host injects them.
await bootstrapGogAuth();

await runMcp({

Static analysis

Detected: suspicious.dangerous_exec, suspicious.dynamic_code_execution, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
dist/index.js:35634

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
dist/index.js:27941

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
dist/index.js:34953