-
Notifications
You must be signed in to change notification settings - Fork 43
feat: add Go framework support #945
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
Changes from all commits
5b9202e
f0ce718
3e8f272
851fc01
841fdc2
b7ca369
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,101 @@ | ||
| /* Go wizard using posthog-agent with PostHog MCP */ | ||
| import * as fs from 'node:fs'; | ||
| import * as path from 'node:path'; | ||
| import type { WizardRunOptions } from '@utils/types'; | ||
| import type { FrameworkConfig } from '@lib/framework-config'; | ||
| import { goModulesPackageManager } from '@lib/detection/package-manager'; | ||
| import { Integration } from '@lib/constants'; | ||
|
|
||
| type GoContext = { | ||
| goVersion?: string; | ||
| }; | ||
|
|
||
| function readGoMod(installDir: string): string | undefined { | ||
| const goModPath = path.join(installDir, 'go.mod'); | ||
| if (!fs.existsSync(goModPath)) { | ||
| return undefined; | ||
| } | ||
| return fs.readFileSync(goModPath, 'utf-8'); | ||
| } | ||
|
|
||
| /** The `go 1.x` directive from go.mod (the toolchain floor, not a patch version). */ | ||
| function getGoVersion(installDir: string): string | undefined { | ||
| const goMod = readGoMod(installDir); | ||
| return goMod?.match(/^go\s+([\d.]+)/m)?.[1]; | ||
| } | ||
|
|
||
| export const GO_AGENT_CONFIG: FrameworkConfig<GoContext> = { | ||
| metadata: { | ||
| name: 'Go', | ||
| integration: Integration.go, | ||
| docsUrl: 'https://posthog.com/docs/libraries/go', | ||
| gatherContext: (options: WizardRunOptions) => { | ||
| const goVersion = getGoVersion(options.installDir); | ||
| return Promise.resolve({ goVersion }); | ||
| }, | ||
| }, | ||
|
|
||
| detection: { | ||
| packageName: 'posthog-go', | ||
| packageDisplayName: 'Go', | ||
| usesPackageJson: false, | ||
| getVersion: () => undefined, | ||
| // A go.mod with a module directive marks a Go module root; a bare | ||
| // directory containing .go files without one is not integratable. | ||
| detect: (options) => { | ||
| const goMod = readGoMod(options.installDir); | ||
| return Promise.resolve(!!goMod && /^module\s+\S+/m.test(goMod)); | ||
| }, | ||
| detectPackageManager: goModulesPackageManager, | ||
| }, | ||
|
|
||
| environment: { | ||
| uploadToHosting: false, | ||
| getEnvVars: (apiKey: string, host: string) => ({ | ||
| POSTHOG_API_KEY: apiKey, | ||
| POSTHOG_HOST: host, | ||
| }), | ||
| }, | ||
|
|
||
| analytics: { | ||
| getTags: (context) => ({ | ||
| goVersion: context.goVersion || 'unknown', | ||
| }), | ||
| }, | ||
|
|
||
| prompts: { | ||
| projectTypeDetection: | ||
| 'This is a Go project. Look for go.mod, go.sum, cmd/, internal/, and main packages to confirm.', | ||
| packageInstallation: | ||
| 'Install the PostHog Go SDK with `go get github.com/posthog/posthog-go`. Do not manually edit go.mod or go.sum; the go tool updates them automatically. Run `go mod tidy` afterwards if imports change.', | ||
| getAdditionalContextLines: (context) => { | ||
| const lines = [ | ||
| `Framework docs ID: go (use posthog://docs/frameworks/go for documentation)`, | ||
| ]; | ||
| if (context.goVersion) { | ||
| lines.push(`Go version (go.mod directive): ${context.goVersion}`); | ||
| } | ||
| lines.push( | ||
| 'Create one PostHog client per process and close it during graceful shutdown (`defer client.Close()`) so queued events flush.', | ||
| ); | ||
| return lines; | ||
| }, | ||
| }, | ||
|
|
||
| ui: { | ||
| successMessage: 'PostHog integration complete', | ||
| estimatedDurationMinutes: 5, | ||
| getOutroChanges: () => [ | ||
| 'Analyzed your Go project structure', | ||
| 'Installed the posthog-go SDK via go get', | ||
| 'Initialized a shared PostHog client configured from environment variables', | ||
| 'Instrumented meaningful server events with client.Enqueue(posthog.Capture{...})', | ||
| ], | ||
| getOutroNextSteps: () => [ | ||
| 'Run your Go service and trigger the instrumented code paths', | ||
| 'Visit your PostHog dashboard to see incoming events', | ||
| 'Use client.Enqueue(posthog.Capture{...}) to track custom events', | ||
| 'Keep client.Close() in your graceful shutdown path so queued events flush', | ||
| ], | ||
| }, | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -93,6 +93,11 @@ const SIMPLE_MANAGERS: Record<string, readonly string[]> = { | |||||
| xcodegen: ['generate'], | ||||||
| }; | ||||||
|
|
||||||
| // go's verb list is closed: dependency + verify commands only. run/test/generate | ||||||
| // execute project-defined code; `go mod edit` can rewrite module requirements | ||||||
| // to arbitrary sources, so only the read/refresh mod subcommands are allowed. | ||||||
| const GO_SUBCOMMANDS = ['get', 'build', 'vet', 'fmt', 'version', 'list']; | ||||||
| const GO_MOD_SUBCOMMANDS = ['tidy', 'download', 'verify', 'graph', 'why']; | ||||||
| // `pub run` executes arbitrary packages, so it is excluded like npm exec. | ||||||
| const PUB_SUBCOMMANDS = ['add', 'remove', 'get', 'upgrade', 'outdated', 'deps']; | ||||||
| // flutter/dart verbs beyond `pub`. build (native build scripts) and analyze | ||||||
|
|
@@ -120,7 +125,8 @@ const ALLOWED_TOOLS_SUMMARY = | |||||
| 'gem (install|uninstall|list|search), swift (package|build), pod (install|update|search), carthage (bootstrap|update), ' + | ||||||
| 'xcodegen (generate), xcodebuild (build/clean/archive actions), gradle/gradlew (build|clean|dependencies|assemble*/compile*/bundle*/lint* tasks), ' + | ||||||
| 'mvn (install|compile|package|verify|dependency:tree), ' + | ||||||
| 'flutter/dart (pub add/remove/get/upgrade/outdated/deps, analyze, build, clean, doctor).'; | ||||||
| 'flutter/dart (pub add/remove/get/upgrade/outdated/deps, analyze, build, clean, doctor), ' + | ||||||
| 'go (get|build|vet|fmt|version|list, mod tidy/download/verify/graph/why).'; | ||||||
|
|
||||||
| function deny(analyticsReason: string, message: string): BashFenceDecision { | ||||||
| return { allowed: false, message, analyticsReason }; | ||||||
|
|
@@ -344,6 +350,37 @@ function commandDecision(command: string): BashFenceDecision { | |||||
| )}>. run/test execute arbitrary code and are not allowed.`, | ||||||
| ); | ||||||
| } | ||||||
| if (bin === 'go') { | ||||||
| if (parts[1] === 'mod') { | ||||||
| if (parts[2] && GO_MOD_SUBCOMMANDS.includes(parts[2])) | ||||||
| return { allowed: true }; | ||||||
| return denyCommand( | ||||||
| command, | ||||||
| `Allowed go mod subcommands: ${GO_MOD_SUBCOMMANDS.join(', ')}.`, | ||||||
| ); | ||||||
| } | ||||||
| if (parts[1] && GO_SUBCOMMANDS.includes(parts[1])) { | ||||||
| // -toolexec runs an arbitrary program on every build/vet action, so the | ||||||
| // allowed verbs above are not safe with it. Deny it explicitly. | ||||||
| const toolexec = parts | ||||||
| .slice(2) | ||||||
| .find((p) => p === '-toolexec' || p.startsWith('-toolexec=')); | ||||||
|
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. Critical: Arbitrary program execution through Go tool flags A malicious repository can include an executable payload and prompt the agent to run
Suggested change
|
||||||
| if (toolexec) | ||||||
| return denyCommand( | ||||||
| command, | ||||||
| 'The -toolexec flag runs an arbitrary program during the build and is not allowed.', | ||||||
| ); | ||||||
| return { allowed: true }; | ||||||
| } | ||||||
| return denyCommand( | ||||||
| command, | ||||||
| `Allowed go subcommands: ${GO_SUBCOMMANDS.join( | ||||||
| ', ', | ||||||
| )}, mod <${GO_MOD_SUBCOMMANDS.join( | ||||||
| '|', | ||||||
| )}>. go run/test/generate execute project code and are not allowed.`, | ||||||
| ); | ||||||
| } | ||||||
| if (bin === 'uv' && parts[1] === 'pip') { | ||||||
| if (parts[2] && PIP_SUBCOMMANDS.includes(parts[2])) | ||||||
| return { allowed: true }; | ||||||
|
|
||||||
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.
Medium: Build output can install executable Git hooks
Allowing
go buildwith unrestricted flags admits commands such asgo build -o .git/hooks/pre-commit ./cmd/payload. An attacker-controlled repository can use this to install its payload as an executable hook that runs during a later commit; reject-oor validate that its destination cannot target.gitor other executable control paths.