-
Notifications
You must be signed in to change notification settings - Fork 57.3k
optimization track-skill-usage.mjs #28589
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| #!/usr/bin/env node | ||
| "use strict"; | ||
|
|
||
| /** | ||
| * n8n Claude Hook Telemetry (safe + non-blocking) | ||
| * Reads JSON from stdin and sends anonymized analytics. | ||
| */ | ||
|
|
||
| import { createHash } from "node:crypto"; | ||
| import { hostname, userInfo, platform, arch, release } from "node:os"; | ||
|
|
||
| // ---- Constants ---- | ||
| const TELEMETRY_HOST = "https://telemetry.n8n.io"; | ||
| const TELEMETRY_WRITE_KEY = "1zPn7YoGC3ZXE9zLeTKLuQCB4F6"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Custom agent: Security Review Security Review (Credential & Secret Handling): do not hardcode API/write keys in source. Load the telemetry write key from runtime configuration instead. Prompt for AI agents |
||
| const TIMEOUT_MS = 1500; | ||
|
|
||
| // ---- Helpers ---- | ||
| function readStdin() { | ||
| return new Promise((resolve) => { | ||
| let data = ""; | ||
| process.stdin.setEncoding("utf8"); | ||
| process.stdin.on("data", (chunk) => (data += chunk)); | ||
| process.stdin.on("end", () => resolve(data.trim())); | ||
| process.stdin.on("error", () => resolve("")); | ||
| }); | ||
| } | ||
|
|
||
| function safeJsonParse(text) { | ||
| try { | ||
| return JSON.parse(text); | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| function isValidSkill(skill) { | ||
| return ( | ||
| typeof skill === "string" && | ||
| (skill.startsWith("n8n:") || skill.startsWith("n8n-")) | ||
| ); | ||
| } | ||
|
|
||
| function createAnonymousId() { | ||
| try { | ||
| const raw = `${userInfo().username}@${hostname()}|${platform()}|${arch()}|${release()}`; | ||
| return createHash("sha256").update(raw).digest("hex"); | ||
| } catch { | ||
| return "unknown"; | ||
| } | ||
| } | ||
|
|
||
| async function sendTelemetry(payload) { | ||
| // Abort if fetch not available (older Node) | ||
| if (typeof fetch !== "function") return; | ||
|
|
||
| const controller = new AbortController(); | ||
| const timer = setTimeout(() => controller.abort(), TIMEOUT_MS); | ||
|
|
||
| try { | ||
| await fetch(`${TELEMETRY_HOST}/v1/track`, { | ||
| method: "POST", | ||
| signal: controller.signal, | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Authorization: | ||
| "Basic " + Buffer.from(`${TELEMETRY_WRITE_KEY}:`).toString("base64"), | ||
| }, | ||
| body: JSON.stringify(payload), | ||
| }); | ||
| } catch { | ||
| // Never fail user workflow | ||
| } finally { | ||
| clearTimeout(timer); | ||
| } | ||
| } | ||
|
|
||
| // ---- Main ---- | ||
| (async () => { | ||
| try { | ||
| const inputText = await readStdin(); | ||
| if (!inputText) process.exit(0); | ||
|
|
||
| const input = safeJsonParse(inputText); | ||
| if (!input?.tool_input?.skill) process.exit(0); | ||
|
|
||
| const skillName = input.tool_input.skill; | ||
| if (!isValidSkill(skillName)) process.exit(0); | ||
|
|
||
| const payload = { | ||
| userId: createAnonymousId(), | ||
| event: "Claude Code skill activated", | ||
| properties: { skill: skillName }, | ||
| context: { ip: "0.0.0.0" }, | ||
| }; | ||
|
|
||
| // Fire and forget | ||
| sendTelemetry(payload); | ||
| } catch { | ||
| // Absolute fail-safe | ||
| } finally { | ||
| process.exit(0); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Forced Prompt for AI agents |
||
| } | ||
| })(); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exposed secret in .claude/plugins/n8n/scripts/optimization track-skill-usage.mjs - high severity
Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
Reply
@AikidoSec ignore: [REASON]to ignore this issue.More Info