Skip to main content
Back to skill

Security audit results

Plan2Meal · clawdhub/plan2meal

Version

1.0.1

Status: pending_audit
Created: 1/16/2026, 8:01:08 PM
Latest
aiWARN
Risk: mediumStarted: 1/16/2026, 8:01:53 PMCompleted: 1/16/2026, 8:02:04 PM
View report details
{
  "notes": "No obvious exfiltration, obfuscation, filesystem abuse, or prompt injection found. Skill handles session tokens for backend authentication.",
  "risks": [
    {
      "category": "network",
      "evidence": "Uses fetch to `${this.apiUrl}` with default `https://gallant-bass-875.convex.site` in src/index.ts and dist/index.js; OAuth calls to `https://github.com/login/device/code` and `https://github.com/login/oauth/access_token` in src/github-oauth.ts/dist/github-oauth.js.",
      "severity": "medium",
      "recommendation": "Declare required/optional network endpoints and environment variables in the manifest; document and allowlist backend domains; avoid hardcoded default API URL or make it explicit and configurable."
    },
    {
      "category": "network",
      "evidence": "Environment variable PLAN2MEAL_API_URL is used but dependencyReport shows no env declarations.",
      "severity": "low",
      "recommendation": "Add PLAN2MEAL_API_URL (and PLAN2MEAL_AUTH_URL if used) to manifest env requirements to make network usage explicit."
    }
  ],
  "summary": "Skill performs network requests to Plan2Meal backend and GitHub OAuth endpoints with a hardcoded default API URL; environment variables for endpoints are not declared in manifest.",
  "verdict": "warn",
  "allowNetwork": [
    "gallant-bass-875.convex.site",
    "github.com",
    "api.plan2meal.app",
    "app.plan2meal.com"
  ],
  "requireHumanReview": true
}
dependencyWARN
Risk: mediumStarted: 1/16/2026, 8:01:53 PMCompleted: 1/16/2026, 8:01:53 PM
View report details
{
  "requires": {
    "env": [],
    "bins": [],
    "config": [],
    "anyBins": []
  },
  "installers": [],
  "references": {
    "tools": [],
    "connectors": []
  },
  "dependencies": []
}
licensePASS
Risk: lowStarted: n/aCompleted: n/a
View report details
{
  "license": "MIT",
  "allowlisted": true
}
metadataPASS
Risk: lowStarted: n/aCompleted: n/a
View report details
{
  "name": "Plan2Meal",
  "type": "tool",
  "license": "MIT",
  "version": "1.0.1",
  "homepage": "https://clawdhub.com/okikeSolutions/plan2meal",
  "security": {
    "openSource": true,
    "safeListed": false,
    "auditRequired": true,
    "requireSource": false,
    "requiresAudit": true,
    "repositoryHost": null,
    "declaredOpenSource": true,
    "declaredAuditRequired": null,
    "repositoryHostAllowed": null
  },
  "repository": null,
  "description": "Manage recipes and grocery lists via Plan2Meal, a React Native recipe app. Add recipes from URLs, search and organize your collection, and create shopping lists.",
  "sourceBytes": 44990,
  "capabilities": [
    "recipe-management",
    "grocery-list-management",
    "url-content-extraction"
  ],
  "sourceCommit": null,
  "sourceSha256": "20cc4ad2b1d3313bcc0e176bf7a316b34bb7a078a6d35ffb7fa3df4c271463dd"
}
sandboxPASS
Risk: lowStarted: 1/16/2026, 8:01:53 PMCompleted: 1/16/2026, 8:01:53 PM
View report details
{
  "reason": "sandbox deferred in v1",
  "skipped": true
}
staticPASS
Risk: lowStarted: 1/16/2026, 8:01:53 PMCompleted: 1/16/2026, 8:01:53 PM
View report details
{
  "flags": [],
  "fileCount": 26,
  "sourceScan": {
    "totalFiles": 26,
    "scannedFiles": 26,
    "skippedBytes": 0,
    "skippedFiles": 0,
    "suspiciousFiles": [
      {
        "path": "README.md",
        "reasons": [
          "network"
        ],
        "excerpts": [
          "A ClawdHub skill for managing recipes and grocery lists via chat. Connects to [Plan2Meal](https://plan2meal.app), a React Native recipe app."
        ]
      },
      {
        "path": "SKILL.md",
        "reasons": [
          "network",
          "secrets"
        ],
        "excerpts": [
          "- **Recipe Extraction**: Automatically fetch recipe metadata from URLs",
          "   - `PLAN2MEAL_API_URL`: Your Plan2Meal backend API URL (e.g., `https://api.plan2meal.app`)",
          "   - `PLAN2MEAL_AUTH_URL`: Custom authentication URL (defaults to `https://app.plan2meal.com/sign-in`)",
          "   - **Authentication**: Users authenticate via your Plan2Meal web app, then copy a session token back to Telegram.",
          "5. After successful authentication, your backend shows the user a session token",
          "6. User copies the token and sends it back to Telegram (or types `token: <token>`)"
        ]
      },
      {
        "path": "dist/convex.d.ts",
        "reasons": [
          "network"
        ],
        "excerpts": [
          "     * Make HTTP request to Plan2Meal backend API",
          "    private request;",
          "     * Fetch recipe metadata from URL via backend"
        ]
      },
      {
        "path": "dist/convex.js",
        "reasons": [
          "network"
        ],
        "excerpts": [
          "     * Make HTTP request to Plan2Meal backend API",
          "    async request(path, options) {",
          "        const response = await fetch(`${this.apiUrl}${path}`, {"
        ]
      },
      {
        "path": "dist/device-auth.d.ts",
        "reasons": [
          "secrets"
        ],
        "excerpts": [
          "        token: string;",
          "    token: string;",
          "     * Refresh access token using refresh token"
        ]
      },
      {
        "path": "dist/device-auth.js",
        "reasons": [
          "network",
          "secrets"
        ],
        "excerpts": [
          "        const response = await fetch(`${this.apiUrl}/auth/device/start`, {",
          "        const response = await fetch(`${this.apiUrl}/auth/device/poll`, {",
          "        const response = await fetch(`${this.apiUrl}/auth/refresh`, {",
          "     * Refresh access token using refresh token",
          "     * Validate an existing token",
          "    async validateToken(token) {"
        ]
      },
      {
        "path": "dist/github-oauth.d.ts",
        "reasons": [
          "secrets"
        ],
        "excerpts": [
          "     * Poll for access token after user authorizes",
          "     * Exchange GitHub token for Plan2Meal session token",
          "     * Full device flow: start, wait for user, exchange token"
        ]
      },
      {
        "path": "dist/github-oauth.js",
        "reasons": [
          "network",
          "secrets"
        ],
        "excerpts": [
          "        const response = await fetch('https://github.com/login/device/code', {",
          "            const response = await fetch('https://github.com/login/oauth/access_token', {",
          "        const response = await fetch(`${this.apiUrl}/api/auth/github/exchange`, {",
          "     * Poll for access token after user authorizes",
          "     * Exchange GitHub token for Plan2Meal session token",
          "            throw new Error(`Failed to exchange token: ${error}`);"
        ]
      },
      {
        "path": "dist/index.d.ts",
        "reasons": [
          "secrets"
        ],
        "excerpts": [
          "declare function setUserToken(sessionId: string, token: string): void;"
        ]
      },
      {
        "path": "dist/index.js",
        "reasons": [
          "network",
          "secrets"
        ],
        "excerpts": [
          "    apiUrl: process.env.PLAN2MEAL_API_URL || 'https://gallant-bass-875.convex.site',",
          "function getApiClient(token) {",
          "    return new convex_1.default(config.apiUrl, token);",
          "function getCommands(token) {"
        ]
      },
      {
        "path": "dist/test.js",
        "reasons": [
          "network"
        ],
        "excerpts": [
          "    apiUrl: process.env.PLAN2MEAL_API_URL || 'https://gallant-bass-875.convex.site',"
        ]
      },
      {
        "path": "dist/utils.js",
        "reasons": [
          "crypto"
        ],
        "excerpts": [
          "    crypto.getRandomValues(array);"
        ]
      },
      {
        "path": "package-lock.json",
        "reasons": [
          "network",
          "exec"
        ],
        "excerpts": [
          "      \"resolved\": \"https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz\",",
          "        \"url\": \"https://opencollective.com/eslint\"",
          "      \"resolved\": \"https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz\",",
          "    \"node_modules/cross-spawn\": {",
          "      \"resolved\": \"https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz\",",
          "        \"cross-spawn\": \"^7.0.6\","
        ]
      },
      {
        "path": "src/commands.ts",
        "reasons": [
          "secrets"
        ],
        "excerpts": [
          "    validateToken: (token: string) => Promise<boolean>;"
        ]
      },
      {
        "path": "src/convex.ts",
        "reasons": [
          "network"
        ],
        "excerpts": [
          "   * Make HTTP request to Plan2Meal backend API",
          "  private async request<T>(path: string, options?: RequestInit): Promise<T> {",
          "    const response = await fetch(`${this.apiUrl}${path}`, {"
        ]
      },
      {
        "path": "src/device-auth.ts",
        "reasons": [
          "network",
          "secrets"
        ],
        "excerpts": [
          "    const response = await fetch(`${this.apiUrl}/auth/device/start`, {",
          "    const response = await fetch(`${this.apiUrl}/auth/device/poll`, {",
          "    const response = await fetch(`${this.apiUrl}/auth/refresh`, {",
          "    token: string;",
          "  token: string;",
          "   * Refresh access token using refresh token"
        ]
      },
      {
        "path": "src/index.ts",
        "reasons": [
          "network",
          "secrets"
        ],
        "excerpts": [
          "  apiUrl: process.env.PLAN2MEAL_API_URL || 'https://gallant-bass-875.convex.site',",
          "function getApiClient(token: string) {",
          "  return new Plan2MealApiClient(config.apiUrl, token);",
          "function getCommands(token: string): Plan2MealCommands {"
        ]
      },
      {
        "path": "src/test.ts",
        "reasons": [
          "network"
        ],
        "excerpts": [
          "  apiUrl: process.env.PLAN2MEAL_API_URL || 'https://gallant-bass-875.convex.site',"
        ]
      },
      {
        "path": "src/utils.ts",
        "reasons": [
          "crypto"
        ],
        "excerpts": [
          "  crypto.getRandomValues(array);"
        ]
      }
    ]
  }
}