claude-code-superkit 0.0.0-stage → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +374 -2
- package/bin/cli.js +153 -0
- package/bin/inject-tokens.js +224 -0
- package/bin/measure-tokens.js +17 -0
- package/bin/superkit-counts-verify.sh +30 -0
- package/lib/codex.js +246 -0
- package/lib/docs-scaffold.js +147 -0
- package/lib/installer.js +499 -0
- package/lib/prompts.js +51 -0
- package/lib/settings-builder.js +51 -0
- package/lib/superpowers.js +99 -0
- package/lib/utils.js +117 -0
- package/lib/validator.js +84 -0
- package/package.json +49 -4
- package/packages/codex/AGENTS.md +396 -0
- package/packages/codex/INSTALL.md +342 -0
- package/packages/codex/config.toml +22 -0
- package/packages/codex/hooks/superkit.mjs +203 -0
- package/packages/codex/hooks.json +30 -0
- package/packages/codex/legacy-baseline.json +88 -0
- package/packages/codex/native-skills.txt +65 -0
- package/packages/codex/rules/default.rules +565 -0
- package/packages/codex/skills/ai-slop-cleaner/SKILL.md +44 -0
- package/packages/codex/skills/api-contract-sync/SKILL.md +96 -0
- package/packages/codex/skills/architect/SKILL.md +49 -0
- package/packages/codex/skills/audit-backend/SKILL.md +80 -0
- package/packages/codex/skills/audit-frontend/SKILL.md +82 -0
- package/packages/codex/skills/audit-infra/SKILL.md +87 -0
- package/packages/codex/skills/audit-orchestrator/SKILL.md +114 -0
- package/packages/codex/skills/behavioral-nudge-engine/SKILL.md +110 -0
- package/packages/codex/skills/benchmark/SKILL.md +49 -0
- package/packages/codex/skills/bot-reviewer/SKILL.md +94 -0
- package/packages/codex/skills/code-reviewer/SKILL.md +67 -0
- package/packages/codex/skills/codebase-onboarding-engineer/SKILL.md +46 -0
- package/packages/codex/skills/comment-rot-analyzer/SKILL.md +75 -0
- package/packages/codex/skills/commit-helper/SKILL.md +101 -0
- package/packages/codex/skills/critic/SKILL.md +56 -0
- package/packages/codex/skills/database-reviewer/SKILL.md +79 -0
- package/packages/codex/skills/debug-observer/SKILL.md +107 -0
- package/packages/codex/skills/dependency-checker/SKILL.md +69 -0
- package/packages/codex/skills/design-system-reviewer/SKILL.md +121 -0
- package/packages/codex/skills/dev-orchestrator/SKILL.md +304 -0
- package/packages/codex/skills/docs-reviewer/SKILL.md +86 -0
- package/packages/codex/skills/drizzle-orm-expert/SKILL.md +246 -0
- package/packages/codex/skills/e2e-test-generator/SKILL.md +95 -0
- package/packages/codex/skills/evaluator/SKILL.md +63 -0
- package/packages/codex/skills/frontend-perf-reviewer/SKILL.md +95 -0
- package/packages/codex/skills/frontend-ui-color-reviewer/SKILL.md +68 -0
- package/packages/codex/skills/frontend-ui-design-critic/SKILL.md +99 -0
- package/packages/codex/skills/frontend-ui-interaction-reviewer/SKILL.md +102 -0
- package/packages/codex/skills/frontend-ui-motion-reviewer/SKILL.md +85 -0
- package/packages/codex/skills/frontend-ui-reviewer/SKILL.md +81 -0
- package/packages/codex/skills/frontend-ui-typography-reviewer/SKILL.md +62 -0
- package/packages/codex/skills/gltf-debugging/SKILL.md +128 -0
- package/packages/codex/skills/go-benchmark/SKILL.md +217 -0
- package/packages/codex/skills/go-concurrency-reviewer/SKILL.md +113 -0
- package/packages/codex/skills/go-error-reviewer/SKILL.md +107 -0
- package/packages/codex/skills/go-grpc-patterns/SKILL.md +285 -0
- package/packages/codex/skills/go-modernizer/SKILL.md +111 -0
- package/packages/codex/skills/go-observability-reviewer/SKILL.md +121 -0
- package/packages/codex/skills/go-performance-reviewer/SKILL.md +112 -0
- package/packages/codex/skills/go-reviewer/SKILL.md +109 -0
- package/packages/codex/skills/go-samber-do/SKILL.md +158 -0
- package/packages/codex/skills/go-samber-lo/SKILL.md +178 -0
- package/packages/codex/skills/go-samber-oops/SKILL.md +174 -0
- package/packages/codex/skills/goal-verifier/SKILL.md +53 -0
- package/packages/codex/skills/health-checker/SKILL.md +109 -0
- package/packages/codex/skills/html-to-3d-texture/SKILL.md +153 -0
- package/packages/codex/skills/impeccable-craft/SKILL.md +101 -0
- package/packages/codex/skills/lint-runner/SKILL.md +85 -0
- package/packages/codex/skills/migrate/SKILL.md +60 -0
- package/packages/codex/skills/migration-reviewer/SKILL.md +88 -0
- package/packages/codex/skills/minimal-change-engineer/SKILL.md +44 -0
- package/packages/codex/skills/new-migration/SKILL.md +59 -0
- package/packages/codex/skills/nextjs-supabase-auth/SKILL.md +227 -0
- package/packages/codex/skills/output-enforcement/SKILL.md +71 -0
- package/packages/codex/skills/plan-checker/SKILL.md +53 -0
- package/packages/codex/skills/postgresql-optimization/SKILL.md +159 -0
- package/packages/codex/skills/pre-deploy-validator/SKILL.md +111 -0
- package/packages/codex/skills/presentation-reviewer/SKILL.md +88 -0
- package/packages/codex/skills/product-3d-lighting/SKILL.md +137 -0
- package/packages/codex/skills/project-architecture/SKILL.md +52 -0
- package/packages/codex/skills/py-reviewer/SKILL.md +100 -0
- package/packages/codex/skills/r3f-scene-reviewer/SKILL.md +88 -0
- package/packages/codex/skills/r3f-scroll-driven-3d/SKILL.md +162 -0
- package/packages/codex/skills/reality-checker/SKILL.md +61 -0
- package/packages/codex/skills/redis-patterns/SKILL.md +260 -0
- package/packages/codex/skills/review-orchestrator/SKILL.md +151 -0
- package/packages/codex/skills/rs-reviewer/SKILL.md +98 -0
- package/packages/codex/skills/ru-text/SKILL.md +132 -0
- package/packages/codex/skills/scaffold-endpoint/SKILL.md +44 -0
- package/packages/codex/skills/security-scanner/SKILL.md +94 -0
- package/packages/codex/skills/silent-failure-hunter/SKILL.md +176 -0
- package/packages/codex/skills/telegram-bot-builder/SKILL.md +120 -0
- package/packages/codex/skills/test-generator/SKILL.md +79 -0
- package/packages/codex/skills/test-runner/SKILL.md +101 -0
- package/packages/codex/skills/threejs-color-management/SKILL.md +97 -0
- package/packages/codex/skills/tree-generator/SKILL.md +63 -0
- package/packages/codex/skills/ts-reviewer/SKILL.md +90 -0
- package/packages/codex/skills/ui-design-reviewer/SKILL.md +89 -0
- package/packages/codex/skills/ui-reviewer/SKILL.md +103 -0
- package/packages/codex/skills/visual-reviewer/SKILL.md +85 -0
- package/packages/codex/skills/writing-agents/SKILL.md +165 -0
- package/packages/codex/skills/writing-commands/SKILL.md +173 -0
- package/packages/core/CLAUDE.md +118 -0
- package/packages/core/INTERNAL-FILES +16 -0
- package/packages/core/agents/ai-slop-cleaner.md +187 -0
- package/packages/core/agents/api-contract-sync.md +127 -0
- package/packages/core/agents/architect.md +205 -0
- package/packages/core/agents/audit-backend.md +137 -0
- package/packages/core/agents/audit-frontend.md +129 -0
- package/packages/core/agents/audit-infra.md +182 -0
- package/packages/core/agents/behavioral-nudge-engine.md +170 -0
- package/packages/core/agents/code-reviewer.md +195 -0
- package/packages/core/agents/codebase-onboarding-engineer.md +172 -0
- package/packages/core/agents/comment-rot-analyzer.md +115 -0
- package/packages/core/agents/critic.md +172 -0
- package/packages/core/agents/database-reviewer.md +201 -0
- package/packages/core/agents/debug-observer.md +261 -0
- package/packages/core/agents/dependency-checker.md +199 -0
- package/packages/core/agents/docs-reviewer.md +206 -0
- package/packages/core/agents/e2e-test-generator.md +261 -0
- package/packages/core/agents/evaluator.md +141 -0
- package/packages/core/agents/goal-verifier.md +130 -0
- package/packages/core/agents/health-checker.md +194 -0
- package/packages/core/agents/migration-reviewer.md +161 -0
- package/packages/core/agents/minimal-change-engineer.md +219 -0
- package/packages/core/agents/plan-checker.md +139 -0
- package/packages/core/agents/pre-deploy-validator.md +189 -0
- package/packages/core/agents/reality-checker.md +175 -0
- package/packages/core/agents/scaffold-endpoint.md +162 -0
- package/packages/core/agents/security-scanner.md +236 -0
- package/packages/core/agents/silent-failure-hunter.md +276 -0
- package/packages/core/agents/test-generator.md +146 -0
- package/packages/core/agents/tree-generator.md +141 -0
- package/packages/core/agents/ui-reviewer.md +172 -0
- package/packages/core/agents/visual-reviewer.md +211 -0
- package/packages/core/commands/audit.md +166 -0
- package/packages/core/commands/benchmark.md +130 -0
- package/packages/core/commands/commit.md +148 -0
- package/packages/core/commands/dev.md +302 -0
- package/packages/core/commands/docs-init.md +75 -0
- package/packages/core/commands/lint.md +137 -0
- package/packages/core/commands/migrate.md +146 -0
- package/packages/core/commands/new-migration.md +182 -0
- package/packages/core/commands/pair.md +209 -0
- package/packages/core/commands/review.md +195 -0
- package/packages/core/commands/security-scan.md +142 -0
- package/packages/core/commands/superkit-evolve.md +231 -0
- package/packages/core/commands/superkit-init.md +274 -0
- package/packages/core/commands/test.md +108 -0
- package/packages/core/commands/workflow.md +150 -0
- package/packages/core/docs/guide/01-getting-started.md +41 -0
- package/packages/core/docs-templates/README.md +33 -0
- package/packages/core/docs-templates/adr-template.md +62 -0
- package/packages/core/docs-templates/architecture/api-contracts.md +26 -0
- package/packages/core/docs-templates/architecture/api-reference.md +48 -0
- package/packages/core/docs-templates/architecture/auth-and-sessions.md +38 -0
- package/packages/core/docs-templates/architecture/backend-layers.md +42 -0
- package/packages/core/docs-templates/architecture/data-flow.md +26 -0
- package/packages/core/docs-templates/architecture/database-schema.md +51 -0
- package/packages/core/docs-templates/architecture/deployment.md +41 -0
- package/packages/core/docs-templates/architecture/frontend-state.md +46 -0
- package/packages/core/helpers/statusline.cjs +370 -0
- package/packages/core/hooks/CHAIN-SEMANTICS.md +84 -0
- package/packages/core/hooks/audit-settings-source.sh +98 -0
- package/packages/core/hooks/audit-trail.sh +120 -0
- package/packages/core/hooks/block-dangerous-git.sh +189 -0
- package/packages/core/hooks/bundle-import-check.sh +64 -0
- package/packages/core/hooks/compact-state-inject.sh +84 -0
- package/packages/core/hooks/config-protection.sh +68 -0
- package/packages/core/hooks/console-log-warning.sh +28 -0
- package/packages/core/hooks/context-monitor.sh +35 -0
- package/packages/core/hooks/dev-edit-counter.sh +59 -0
- package/packages/core/hooks/dev-marker-set.sh +55 -0
- package/packages/core/hooks/dev-required-on-commit.sh +426 -0
- package/packages/core/hooks/doc-check-on-commit.sh +396 -0
- package/packages/core/hooks/edit-streak-check.sh +63 -0
- package/packages/core/hooks/evolve-check.sh +78 -0
- package/packages/core/hooks/gateguard-pre-edit.sh +154 -0
- package/packages/core/hooks/gateguard-record-facts.sh +37 -0
- package/packages/core/hooks/intake-classifier.py +449 -0
- package/packages/core/hooks/lib/profile.sh +141 -0
- package/packages/core/hooks/loop-guard.sh +75 -0
- package/packages/core/hooks/migration-safety.sh +56 -0
- package/packages/core/hooks/plan-completion-gate.sh +61 -0
- package/packages/core/hooks/pre-compact-save.sh +89 -0
- package/packages/core/hooks/security-patterns.sh +119 -0
- package/packages/core/hooks/session-context-restore.sh +76 -0
- package/packages/core/hooks/subagent-stop-validate.sh +73 -0
- package/packages/core/hooks/superkit-counts-verify.sh +346 -0
- package/packages/core/hooks/superkit-update.sh +367 -0
- package/packages/core/hooks/tests/README.md +42 -0
- package/packages/core/hooks/tests/audit-trail-chain_test.sh +62 -0
- package/packages/core/hooks/tests/block-dangerous-git_test.sh +144 -0
- package/packages/core/hooks/tests/chain-semantics/h1.sh +11 -0
- package/packages/core/hooks/tests/chain-semantics/h2.sh +9 -0
- package/packages/core/hooks/tests/chain-semantics/h3.sh +11 -0
- package/packages/core/hooks/tests/chain-semantics/run-experiment.sh +65 -0
- package/packages/core/hooks/tests/chain-semantics/settings-experiment.json +15 -0
- package/packages/core/hooks/tests/counts-verify_test.sh +118 -0
- package/packages/core/hooks/tests/dev-required-on-commit_test.sh +207 -0
- package/packages/core/hooks/tests/dev-required-wip-on-main_test.sh +20 -0
- package/packages/core/hooks/tests/doc-check-on-commit_test.sh +122 -0
- package/packages/core/hooks/tests/edit-streak-check_test.sh +106 -0
- package/packages/core/hooks/tests/internal-manifest_test.sh +130 -0
- package/packages/core/hooks/tests/json-path_test.sh +108 -0
- package/packages/core/hooks/tests/mirror-invariants_test.sh +139 -0
- package/packages/core/hooks/tests/nudge-throttle_test.sh +95 -0
- package/packages/core/hooks/tests/profile-helper_test.sh +21 -0
- package/packages/core/hooks/tests/run-all.sh +47 -0
- package/packages/core/hooks/tests/session-key-helper_test.sh +17 -0
- package/packages/core/hooks/tests/superkit-update_test.sh +311 -0
- package/packages/core/hooks/tests/warn-visibility_test.sh +59 -0
- package/packages/core/hooks/user-intent-detect.sh +44 -0
- package/packages/core/hooks/verify-hooks.sh +69 -0
- package/packages/core/rules/auto-commands.md +124 -0
- package/packages/core/rules/coding-style.md +68 -0
- package/packages/core/rules/dev-workflow.md +77 -0
- package/packages/core/rules/documentation.md +106 -0
- package/packages/core/rules/frontend-aesthetics.md +53 -0
- package/packages/core/rules/git-workflow.md +14 -0
- package/packages/core/rules/security.md +29 -0
- package/packages/core/rules/superkit-integrity.md +114 -0
- package/packages/core/settings.json +229 -0
- package/packages/core/skills/drizzle-orm-expert/SKILL.md +260 -0
- package/packages/core/skills/nextjs-supabase-auth/SKILL.md +259 -0
- package/packages/core/skills/postgresql-optimization/SKILL.md +150 -0
- package/packages/core/skills/project-architecture/SKILL.md +31 -0
- package/packages/core/skills/project-scanner/SKILL.md +132 -0
- package/packages/core/skills/redis-patterns/SKILL.md +261 -0
- package/packages/core/skills/ru-text/SKILL.md +156 -0
- package/packages/core/skills/telegram-bot-builder/SKILL.md +145 -0
- package/packages/core/skills/writing-agents/SKILL.md +220 -0
- package/packages/core/skills/writing-commands/SKILL.md +176 -0
- package/packages/core/skills/writing-hooks/SKILL.md +335 -0
- package/packages/extras/bot-reviewer.md +279 -0
- package/packages/extras/design-system-reviewer.md +234 -0
- package/packages/extras/red-blue-auditor/README.md +117 -0
- package/packages/extras/red-blue-auditor/agent.md +128 -0
- package/packages/extras/red-blue-auditor/patterns/agent-config.txt +4 -0
- package/packages/extras/red-blue-auditor/patterns/hook-injection.txt +8 -0
- package/packages/extras/red-blue-auditor/patterns/mcp-risk.txt +5 -0
- package/packages/extras/red-blue-auditor/patterns/permission-abuse.txt +6 -0
- package/packages/extras/red-blue-auditor/patterns/secrets.txt +17 -0
- package/packages/extras/red-blue-auditor/scan.sh +88 -0
- package/packages/extras/skillsmp-search/SKILL.md +134 -0
- package/packages/frontend-3d/README.md +62 -0
- package/packages/frontend-3d/agents/frontend-perf-reviewer.md +164 -0
- package/packages/frontend-3d/agents/presentation-reviewer.md +146 -0
- package/packages/frontend-3d/agents/r3f-scene-reviewer.md +128 -0
- package/packages/frontend-3d/agents/ui-design-reviewer.md +169 -0
- package/packages/frontend-3d/commands/capture-screen.md +166 -0
- package/packages/frontend-3d/hooks/bundle-size-warn.sh +93 -0
- package/packages/frontend-3d/hooks/gsap-pattern-check.sh +74 -0
- package/packages/frontend-3d/hooks/r3f-color-check.sh +76 -0
- package/packages/frontend-3d/hooks/tailwind-version-guard.sh +98 -0
- package/packages/frontend-3d/rules/frontend-aesthetics-3d.md +81 -0
- package/packages/frontend-3d/rules/gsap-conventions.md +129 -0
- package/packages/frontend-3d/rules/threejs-conventions.md +116 -0
- package/packages/frontend-3d/skills/gltf-debugging/SKILL.md +145 -0
- package/packages/frontend-3d/skills/html-to-3d-texture/SKILL.md +183 -0
- package/packages/frontend-3d/skills/output-enforcement/SKILL.md +79 -0
- package/packages/frontend-3d/skills/product-3d-lighting/SKILL.md +166 -0
- package/packages/frontend-3d/skills/r3f-scroll-driven-3d/SKILL.md +209 -0
- package/packages/frontend-3d/skills/threejs-color-management/SKILL.md +111 -0
- package/packages/frontend-ui/NOTICE.md +99 -0
- package/packages/frontend-ui/README.md +59 -0
- package/packages/frontend-ui/agents/ui-color-reviewer.md +174 -0
- package/packages/frontend-ui/agents/ui-design-critic.md +198 -0
- package/packages/frontend-ui/agents/ui-interaction-reviewer.md +259 -0
- package/packages/frontend-ui/agents/ui-motion-reviewer.md +266 -0
- package/packages/frontend-ui/agents/ui-reviewer.md +251 -0
- package/packages/frontend-ui/agents/ui-typography-reviewer.md +222 -0
- package/packages/frontend-ui/hooks/tests/ui-animation-easing-check_test.sh +78 -0
- package/packages/frontend-ui/hooks/tests/ui-banned-fonts-check_test.sh +77 -0
- package/packages/frontend-ui/hooks/tests/ui-color-check_test.sh +75 -0
- package/packages/frontend-ui/hooks/ui-animation-easing-check.sh +86 -0
- package/packages/frontend-ui/hooks/ui-banned-fonts-check.sh +90 -0
- package/packages/frontend-ui/hooks/ui-color-check.sh +102 -0
- package/packages/frontend-ui/rules/color-and-contrast.md +230 -0
- package/packages/frontend-ui/rules/frontend-design-aesthetics.md +124 -0
- package/packages/frontend-ui/rules/interaction-polish.md +335 -0
- package/packages/frontend-ui/rules/motion-and-animation.md +283 -0
- package/packages/frontend-ui/rules/spatial-and-layout.md +212 -0
- package/packages/frontend-ui/rules/typography-guidelines.md +185 -0
- package/packages/frontend-ui/rules/ui-anti-patterns.md +309 -0
- package/packages/frontend-ui/settings.json +24 -0
- package/packages/frontend-ui/skills/impeccable-craft/SKILL.md +187 -0
- package/packages/gan/README.md +232 -0
- package/packages/gan/agents/gan-evaluator.md +214 -0
- package/packages/gan/agents/gan-generator.md +171 -0
- package/packages/gan/agents/gan-planner.md +206 -0
- package/packages/gan/rubrics/functionality.md +66 -0
- package/packages/gan/rubrics/ui-quality.md +77 -0
- package/packages/gan/skills/gan-evaluator/SKILL.md +91 -0
- package/packages/gan/skills/gan-generator/SKILL.md +72 -0
- package/packages/gan/skills/gan-planner/SKILL.md +93 -0
- package/packages/stack-agents/go/go-concurrency-reviewer.md +210 -0
- package/packages/stack-agents/go/go-error-reviewer.md +187 -0
- package/packages/stack-agents/go/go-modernizer.md +160 -0
- package/packages/stack-agents/go/go-observability-reviewer.md +185 -0
- package/packages/stack-agents/go/go-performance-reviewer.md +160 -0
- package/packages/stack-agents/go/go-reviewer.md +277 -0
- package/packages/stack-agents/go/references/benchmark-methodology.md +280 -0
- package/packages/stack-agents/go/references/channel-patterns.md +384 -0
- package/packages/stack-agents/go/references/cli-cobra-viper.md +190 -0
- package/packages/stack-agents/go/references/code-style.md +262 -0
- package/packages/stack-agents/go/references/context-propagation.md +317 -0
- package/packages/stack-agents/go/references/data-structures.md +315 -0
- package/packages/stack-agents/go/references/database-patterns.md +334 -0
- package/packages/stack-agents/go/references/design-patterns.md +411 -0
- package/packages/stack-agents/go/references/di-frameworks.md +146 -0
- package/packages/stack-agents/go/references/error-creation.md +251 -0
- package/packages/stack-agents/go/references/error-inspection.md +348 -0
- package/packages/stack-agents/go/references/error-wrapping.md +230 -0
- package/packages/stack-agents/go/references/gopls-driving.md +62 -0
- package/packages/stack-agents/go/references/goroutine-lifecycle.md +351 -0
- package/packages/stack-agents/go/references/graphql-patterns.md +221 -0
- package/packages/stack-agents/go/references/grpc-patterns.md +373 -0
- package/packages/stack-agents/go/references/modernize-guide.md +522 -0
- package/packages/stack-agents/go/references/module-management.md +191 -0
- package/packages/stack-agents/go/references/naming-conventions.md +176 -0
- package/packages/stack-agents/go/references/observability-pipeline.md +352 -0
- package/packages/stack-agents/go/references/performance-profiling.md +250 -0
- package/packages/stack-agents/go/references/refactoring-mechanics.md +127 -0
- package/packages/stack-agents/go/references/rest-openapi-patterns.md +160 -0
- package/packages/stack-agents/go/references/samber-do.md +201 -0
- package/packages/stack-agents/go/references/samber-libraries.md +363 -0
- package/packages/stack-agents/go/references/samber-lo.md +260 -0
- package/packages/stack-agents/go/references/samber-oops.md +200 -0
- package/packages/stack-agents/go/references/security-checklist.md +441 -0
- package/packages/stack-agents/go/references/standard-stdlib-now.md +181 -0
- package/packages/stack-agents/go/references/stay-updated.md +147 -0
- package/packages/stack-agents/go/references/structs-interfaces.md +312 -0
- package/packages/stack-agents/go/references/sync-primitives.md +395 -0
- package/packages/stack-agents/go/references/testing-patterns.md +483 -0
- package/packages/stack-agents/go/references/troubleshooting.md +98 -0
- package/packages/stack-agents/python/py-reviewer.md +194 -0
- package/packages/stack-agents/rust/rs-reviewer.md +188 -0
- package/packages/stack-agents/typescript/ts-reviewer.md +135 -0
- package/packages/stack-hooks/go/go-context-check-on-edit.sh +56 -0
- package/packages/stack-hooks/go/go-error-check-on-edit.sh +54 -0
- package/packages/stack-hooks/go/go-format-on-edit.sh +32 -0
- package/packages/stack-hooks/go/go-safety-check-on-edit.sh +54 -0
- package/packages/stack-hooks/go/go-vet-on-edit.sh +61 -0
- package/packages/stack-hooks/go/golangci-lint-on-edit.sh +54 -0
- package/packages/stack-hooks/python/ruff-on-edit.sh +67 -0
- package/packages/stack-hooks/rust/cargo-check-on-edit.sh +64 -0
- package/packages/stack-hooks/typescript/typecheck-on-edit.sh +76 -0
- package/packages/stack-rules/go/go-conventions.md +44 -0
- package/packages/stack-rules/go/go-safety.md +35 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
# Security Checklist
|
|
2
|
+
|
|
3
|
+
> Reference document for security-scanner. Loaded on demand via Read tool.
|
|
4
|
+
|
|
5
|
+
## Injection Prevention
|
|
6
|
+
|
|
7
|
+
### SQL Injection
|
|
8
|
+
|
|
9
|
+
**Always use parameterized queries. Never concatenate user input into SQL.**
|
|
10
|
+
|
|
11
|
+
```go
|
|
12
|
+
// CORRECT — parameterized
|
|
13
|
+
row := db.QueryRow(ctx,
|
|
14
|
+
"SELECT id, name FROM users WHERE email = $1", email)
|
|
15
|
+
|
|
16
|
+
// CORRECT — IN clause with pgx
|
|
17
|
+
rows, err := db.Query(ctx,
|
|
18
|
+
"SELECT id, name FROM users WHERE id = ANY($1)", ids)
|
|
19
|
+
|
|
20
|
+
// CRITICAL BUG — SQL injection
|
|
21
|
+
row := db.QueryRow(ctx,
|
|
22
|
+
"SELECT id, name FROM users WHERE email = '" + email + "'")
|
|
23
|
+
|
|
24
|
+
// CRITICAL BUG — fmt.Sprintf with user input
|
|
25
|
+
query := fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", name)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### OS Command Injection
|
|
29
|
+
|
|
30
|
+
```go
|
|
31
|
+
// CRITICAL BUG — user input in exec.Command
|
|
32
|
+
cmd := exec.Command("sh", "-c", "convert "+userFilename+" output.png")
|
|
33
|
+
|
|
34
|
+
// CORRECT — separate arguments, no shell interpretation
|
|
35
|
+
cmd := exec.Command("convert", userFilename, "output.png")
|
|
36
|
+
|
|
37
|
+
// CORRECT — validate input before use
|
|
38
|
+
if !isValidFilename(userFilename) {
|
|
39
|
+
return ErrInvalidFilename
|
|
40
|
+
}
|
|
41
|
+
cmd := exec.Command("convert", userFilename, "output.png")
|
|
42
|
+
|
|
43
|
+
// Validation function
|
|
44
|
+
func isValidFilename(name string) bool {
|
|
45
|
+
// No path traversal, no shell metacharacters
|
|
46
|
+
if strings.Contains(name, "..") || strings.ContainsAny(name, "/\\;|&$`") {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
return regexp.MustCompile(`^[a-zA-Z0-9._-]+$`).MatchString(name)
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### LDAP / XPath Injection
|
|
54
|
+
|
|
55
|
+
```go
|
|
56
|
+
// Escape special characters for LDAP queries
|
|
57
|
+
func ldapEscape(s string) string {
|
|
58
|
+
replacer := strings.NewReplacer(
|
|
59
|
+
`\`, `\5c`, `*`, `\2a`, `(`, `\28`,
|
|
60
|
+
`)`, `\29`, "\x00", `\00`,
|
|
61
|
+
)
|
|
62
|
+
return replacer.Replace(s)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
filter := fmt.Sprintf("(uid=%s)", ldapEscape(username))
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Crypto Best Practices
|
|
69
|
+
|
|
70
|
+
### Password Hashing
|
|
71
|
+
|
|
72
|
+
```go
|
|
73
|
+
import "golang.org/x/crypto/bcrypt"
|
|
74
|
+
|
|
75
|
+
// Hash password (cost 12 is a good default)
|
|
76
|
+
func HashPassword(password string) (string, error) {
|
|
77
|
+
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 12)
|
|
78
|
+
return string(bytes), err
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Verify password
|
|
82
|
+
func CheckPassword(password, hash string) bool {
|
|
83
|
+
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
|
84
|
+
return err == nil
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**bcrypt caps input at 72 bytes.** Current `x/crypto/bcrypt.GenerateFromPassword`
|
|
89
|
+
returns `bcrypt.ErrPasswordTooLong` for longer input — propagate that error (the
|
|
90
|
+
`HashPassword` above already returns it). Do NOT add a `len(pw) > 72` guard:
|
|
91
|
+
truncating silently weakens the hash, and a boundary-specific error message leaks
|
|
92
|
+
the limit to an attacker. For genuinely long passphrases use argon2id/scrypt, or a
|
|
93
|
+
deliberate, documented SHA-256 pre-hash (base64-encode the digest first, so an
|
|
94
|
+
embedded NUL byte can't truncate the input) before bcrypt.
|
|
95
|
+
|
|
96
|
+
**Argon2 (preferred for new projects):**
|
|
97
|
+
|
|
98
|
+
```go
|
|
99
|
+
import "golang.org/x/crypto/argon2"
|
|
100
|
+
|
|
101
|
+
func HashPasswordArgon2(password string, salt []byte) []byte {
|
|
102
|
+
return argon2.IDKey([]byte(password), salt, 3, 64*1024, 4, 32)
|
|
103
|
+
// time=3, memory=64MB, threads=4, keyLen=32
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Stdlib KDFs (Go 1.24+):** `crypto/pbkdf2`, `crypto/hkdf`, and `crypto/sha3` are
|
|
108
|
+
now in the standard library — no `golang.org/x/crypto` import needed for these.
|
|
109
|
+
Password hashing (bcrypt, argon2, scrypt) still lives in `x/crypto`; reach for
|
|
110
|
+
stdlib `pbkdf2.Key` only for legacy/interop, argon2id for new password storage.
|
|
111
|
+
|
|
112
|
+
### Random Number Generation
|
|
113
|
+
|
|
114
|
+
```go
|
|
115
|
+
// CORRECT — crypto/rand for security-sensitive values
|
|
116
|
+
import "crypto/rand"
|
|
117
|
+
|
|
118
|
+
func GenerateToken(length int) (string, error) {
|
|
119
|
+
bytes := make([]byte, length)
|
|
120
|
+
if _, err := rand.Read(bytes); err != nil {
|
|
121
|
+
return "", err
|
|
122
|
+
}
|
|
123
|
+
return base64.URLEncoding.EncodeToString(bytes), nil
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
func GenerateSecureID() (string, error) {
|
|
127
|
+
id := make([]byte, 16)
|
|
128
|
+
if _, err := rand.Read(id); err != nil {
|
|
129
|
+
return "", err
|
|
130
|
+
}
|
|
131
|
+
return hex.EncodeToString(id), nil
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// WRONG — math/rand for security (predictable!)
|
|
135
|
+
import "math/rand"
|
|
136
|
+
token := fmt.Sprintf("%d", rand.Int()) // INSECURE
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### TLS Configuration
|
|
140
|
+
|
|
141
|
+
```go
|
|
142
|
+
tlsConfig := &tls.Config{
|
|
143
|
+
MinVersion: tls.VersionTLS12,
|
|
144
|
+
CipherSuites: []uint16{
|
|
145
|
+
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
146
|
+
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
147
|
+
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
|
148
|
+
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
|
149
|
+
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
150
|
+
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
151
|
+
},
|
|
152
|
+
PreferServerCipherSuites: true,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// WRONG — allows TLS 1.0/1.1
|
|
156
|
+
tlsConfig := &tls.Config{} // defaults allow old versions
|
|
157
|
+
|
|
158
|
+
// WRONG — disables certificate verification
|
|
159
|
+
tlsConfig := &tls.Config{InsecureSkipVerify: true}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Web Security
|
|
163
|
+
|
|
164
|
+
### CORS
|
|
165
|
+
|
|
166
|
+
```go
|
|
167
|
+
func CORSMiddleware(allowedOrigins []string) func(http.Handler) http.Handler {
|
|
168
|
+
originSet := make(map[string]bool)
|
|
169
|
+
for _, o := range allowedOrigins {
|
|
170
|
+
originSet[o] = true
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return func(next http.Handler) http.Handler {
|
|
174
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
175
|
+
origin := r.Header.Get("Origin")
|
|
176
|
+
if originSet[origin] {
|
|
177
|
+
w.Header().Set("Access-Control-Allow-Origin", origin)
|
|
178
|
+
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
|
179
|
+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
|
180
|
+
w.Header().Set("Access-Control-Max-Age", "86400")
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if r.Method == "OPTIONS" {
|
|
184
|
+
w.WriteHeader(http.StatusNoContent)
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
next.ServeHTTP(w, r)
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// WRONG — wildcard origin with credentials
|
|
194
|
+
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
195
|
+
w.Header().Set("Access-Control-Allow-Credentials", "true") // conflict!
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### CSRF (Cross-Origin Protection)
|
|
199
|
+
|
|
200
|
+
Go 1.25+ ships stdlib CSRF protection: `http.CrossOriginProtection` rejects
|
|
201
|
+
non-safe cross-origin browser requests (detected via the `Sec-Fetch-Site` header,
|
|
202
|
+
with an `Origin`/`Host` fallback). Safe methods (GET/HEAD/OPTIONS) stay allowed —
|
|
203
|
+
keep them side-effect-free. Wrap mutating routes with it as middleware; add known
|
|
204
|
+
cross-origin callers with `csrf.AddTrustedOrigin`.
|
|
205
|
+
|
|
206
|
+
```go
|
|
207
|
+
csrf := http.NewCrossOriginProtection()
|
|
208
|
+
handler := csrf.Handler(mux) // rejects cross-origin POST/PUT/PATCH/DELETE
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### API Documentation Exposure
|
|
212
|
+
|
|
213
|
+
Never ship the Swagger/OpenAPI UI publicly in production — gate it behind auth or disable it; an open `/swagger` endpoint maps your entire API surface for an attacker. See `rest-openapi-patterns.md`.
|
|
214
|
+
|
|
215
|
+
### Security Headers
|
|
216
|
+
|
|
217
|
+
```go
|
|
218
|
+
func SecurityHeaders(next http.Handler) http.Handler {
|
|
219
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
220
|
+
w.Header().Set("X-Content-Type-Options", "nosniff")
|
|
221
|
+
w.Header().Set("X-Frame-Options", "DENY")
|
|
222
|
+
w.Header().Set("X-XSS-Protection", "0") // modern browsers: disable, use CSP
|
|
223
|
+
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
|
224
|
+
w.Header().Set("Content-Security-Policy",
|
|
225
|
+
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'")
|
|
226
|
+
w.Header().Set("Strict-Transport-Security",
|
|
227
|
+
"max-age=63072000; includeSubDomains; preload") // HSTS
|
|
228
|
+
|
|
229
|
+
next.ServeHTTP(w, r)
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Secure Cookies
|
|
235
|
+
|
|
236
|
+
```go
|
|
237
|
+
http.SetCookie(w, &http.Cookie{
|
|
238
|
+
Name: "session",
|
|
239
|
+
Value: sessionToken,
|
|
240
|
+
Path: "/",
|
|
241
|
+
HttpOnly: true, // no JavaScript access
|
|
242
|
+
Secure: true, // HTTPS only
|
|
243
|
+
SameSite: http.SameSiteStrictMode, // CSRF protection
|
|
244
|
+
MaxAge: 3600, // 1 hour
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
// WRONG — insecure cookie
|
|
248
|
+
http.SetCookie(w, &http.Cookie{
|
|
249
|
+
Name: "session",
|
|
250
|
+
Value: sessionToken,
|
|
251
|
+
// Missing: HttpOnly, Secure, SameSite
|
|
252
|
+
})
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Auth Patterns
|
|
256
|
+
|
|
257
|
+
### JWT Validation
|
|
258
|
+
|
|
259
|
+
```go
|
|
260
|
+
func ValidateJWT(tokenString string, publicKey *rsa.PublicKey) (*Claims, error) {
|
|
261
|
+
token, err := jwt.ParseWithClaims(tokenString, &Claims{},
|
|
262
|
+
func(token *jwt.Token) (any, error) {
|
|
263
|
+
// Verify signing algorithm
|
|
264
|
+
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
|
265
|
+
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
|
266
|
+
}
|
|
267
|
+
return publicKey, nil
|
|
268
|
+
},
|
|
269
|
+
jwt.WithValidMethods([]string{"RS256"}), // explicit allowed methods
|
|
270
|
+
jwt.WithLeeway(5*time.Second), // clock skew tolerance
|
|
271
|
+
)
|
|
272
|
+
if err != nil {
|
|
273
|
+
return nil, fmt.Errorf("validate token: %w", err)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
claims, ok := token.Claims.(*Claims)
|
|
277
|
+
if !ok || !token.Valid {
|
|
278
|
+
return nil, ErrInvalidToken
|
|
279
|
+
}
|
|
280
|
+
return claims, nil
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// WRONG — no algorithm check (algorithm confusion attack)
|
|
284
|
+
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
|
|
285
|
+
return publicKey, nil // accepts ANY algorithm!
|
|
286
|
+
})
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Rate Limiting
|
|
290
|
+
|
|
291
|
+
```go
|
|
292
|
+
import "golang.org/x/time/rate"
|
|
293
|
+
|
|
294
|
+
type RateLimiter struct {
|
|
295
|
+
mu sync.Mutex
|
|
296
|
+
limiters map[string]*rate.Limiter
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
func (rl *RateLimiter) Allow(key string) bool {
|
|
300
|
+
rl.mu.Lock()
|
|
301
|
+
defer rl.mu.Unlock()
|
|
302
|
+
|
|
303
|
+
limiter, ok := rl.limiters[key]
|
|
304
|
+
if !ok {
|
|
305
|
+
limiter = rate.NewLimiter(rate.Every(time.Second), 10) // 10 req/sec
|
|
306
|
+
rl.limiters[key] = limiter
|
|
307
|
+
}
|
|
308
|
+
return limiter.Allow()
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Input Validation
|
|
313
|
+
|
|
314
|
+
### Boundary Validation
|
|
315
|
+
|
|
316
|
+
```go
|
|
317
|
+
func ValidateCreateUser(req *CreateUserRequest) error {
|
|
318
|
+
var errs []error
|
|
319
|
+
|
|
320
|
+
if len(req.Name) == 0 {
|
|
321
|
+
errs = append(errs, fmt.Errorf("name: required"))
|
|
322
|
+
} else if len(req.Name) > 100 {
|
|
323
|
+
errs = append(errs, fmt.Errorf("name: max 100 characters"))
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if !isValidEmail(req.Email) {
|
|
327
|
+
errs = append(errs, fmt.Errorf("email: invalid format"))
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if req.Age < 0 || req.Age > 150 {
|
|
331
|
+
errs = append(errs, fmt.Errorf("age: must be 0-150"))
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return errors.Join(errs...)
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## File Upload Safety
|
|
339
|
+
|
|
340
|
+
```go
|
|
341
|
+
func HandleUpload(w http.ResponseWriter, r *http.Request) {
|
|
342
|
+
// Size limit
|
|
343
|
+
r.Body = http.MaxBytesReader(w, r.Body, 10<<20) // 10MB max
|
|
344
|
+
|
|
345
|
+
file, header, err := r.FormFile("file")
|
|
346
|
+
if err != nil {
|
|
347
|
+
respondError(w, http.StatusBadRequest, "invalid file")
|
|
348
|
+
return
|
|
349
|
+
}
|
|
350
|
+
defer file.Close()
|
|
351
|
+
|
|
352
|
+
// MIME type validation (read actual content, don't trust header)
|
|
353
|
+
buf := make([]byte, 512)
|
|
354
|
+
n, _ := file.Read(buf)
|
|
355
|
+
mimeType := http.DetectContentType(buf[:n])
|
|
356
|
+
|
|
357
|
+
allowedTypes := map[string]bool{
|
|
358
|
+
"image/jpeg": true, "image/png": true, "image/webp": true,
|
|
359
|
+
}
|
|
360
|
+
if !allowedTypes[mimeType] {
|
|
361
|
+
respondError(w, http.StatusBadRequest, "unsupported file type")
|
|
362
|
+
return
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Reset reader
|
|
366
|
+
file.Seek(0, io.SeekStart)
|
|
367
|
+
|
|
368
|
+
// Filename sanitation (NOT confinement — see os.Root below)
|
|
369
|
+
safeName := filepath.Base(header.Filename) // strip directory components
|
|
370
|
+
if safeName == "." || safeName == "/" {
|
|
371
|
+
respondError(w, http.StatusBadRequest, "invalid filename")
|
|
372
|
+
return
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Save with generated name (never trust user filename)
|
|
376
|
+
storedName := fmt.Sprintf("%s%s", uuid.New().String(), filepath.Ext(safeName))
|
|
377
|
+
// ...
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### os.Root scoped file access (Go 1.24+)
|
|
382
|
+
|
|
383
|
+
`filepath.Base` sanitizes the *name*, but it does not confine the *write*. Once
|
|
384
|
+
you `filepath.Join(uploadDir, name)` — especially if the tree contains a symlink,
|
|
385
|
+
or `name` is rebuilt from other input — an attacker can still escape the directory.
|
|
386
|
+
Confine the operations themselves with `os.OpenRoot`: open the directory once, then
|
|
387
|
+
do every file operation through the returned `*os.Root`. Any name that resolves
|
|
388
|
+
outside the tree — including through a symlink — returns an error instead of
|
|
389
|
+
escaping.
|
|
390
|
+
|
|
391
|
+
```go
|
|
392
|
+
import "os"
|
|
393
|
+
|
|
394
|
+
root, err := os.OpenRoot(uploadDir) // open the upload dir once as a root
|
|
395
|
+
if err != nil {
|
|
396
|
+
return err
|
|
397
|
+
}
|
|
398
|
+
defer root.Close()
|
|
399
|
+
|
|
400
|
+
// root.Create / root.Open / root.OpenFile / root.ReadFile / root.WriteFile all
|
|
401
|
+
// confine to the tree. storedName cannot climb out of uploadDir.
|
|
402
|
+
dst, err := root.Create(storedName)
|
|
403
|
+
if err != nil {
|
|
404
|
+
return err // e.g. name escaped the root, or a symlink pointed outside
|
|
405
|
+
}
|
|
406
|
+
defer dst.Close()
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**Before Go 1.24 (no os.Root):** guard lexically with `filepath.IsLocal`, then
|
|
410
|
+
confirm containment with a separator-aware `filepath.Rel` check — never a raw
|
|
411
|
+
`strings.HasPrefix` on cleaned paths:
|
|
412
|
+
|
|
413
|
+
```go
|
|
414
|
+
if !filepath.IsLocal(name) { // rejects absolute paths, "..", empty, Windows reserved names
|
|
415
|
+
return ErrInvalidFilename
|
|
416
|
+
}
|
|
417
|
+
full := filepath.Join(uploadDir, name)
|
|
418
|
+
rel, err := filepath.Rel(uploadDir, full) // separator-aware containment
|
|
419
|
+
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
|
420
|
+
return ErrInvalidFilename // resolved outside uploadDir
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Explicitly:**
|
|
425
|
+
- `filepath.Clean` + `strings.HasPrefix(path, dir)` is NOT robust confinement — it
|
|
426
|
+
is fooled by prefix collisions (`/data/uploads` vs `/data/uploads-evil`) and is
|
|
427
|
+
blind to symlinks. Use `os.Root`, or `IsLocal` + `Rel` as above — noting the
|
|
428
|
+
`IsLocal`+`Rel` fallback is itself purely lexical (symlink-blind): if the tree
|
|
429
|
+
may contain symlinks, resolve with `filepath.EvalSymlinks` first or require Go 1.24+.
|
|
430
|
+
- `os.Root` is NOT a full sandbox. It blocks path and symlink escape only; per the
|
|
431
|
+
stdlib docs it does **not** protect against traversal of filesystem boundaries,
|
|
432
|
+
Linux bind mounts, `/proc` special files, or Unix device files inside the tree.
|
|
433
|
+
- Keep `filepath.Base` as filename sanitation (strip directory components before
|
|
434
|
+
storing), never as your containment boundary.
|
|
435
|
+
|
|
436
|
+
## When to Use
|
|
437
|
+
|
|
438
|
+
Apply when reviewing any code for security vulnerabilities. Flag violations as:
|
|
439
|
+
- **CRITICAL**: SQL injection, command injection, math/rand for security, InsecureSkipVerify, missing password hashing, wildcard CORS with credentials
|
|
440
|
+
- **WARNING**: Missing security headers, cookies without Secure/HttpOnly, no rate limiting, no input validation
|
|
441
|
+
- **SUGGESTION**: Could use Argon2 instead of bcrypt, add CSP headers, validate MIME types on upload
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Standard Library Replacements
|
|
2
|
+
|
|
3
|
+
Catalog of standard library packages that replace common third-party dependencies. Use this to keep dependency count low and code idiomatic.
|
|
4
|
+
|
|
5
|
+
## Strings and slices
|
|
6
|
+
|
|
7
|
+
| Was: | Now: |
|
|
8
|
+
|------|------|
|
|
9
|
+
| `github.com/elliotchance/orderedmap` | `slices.SortedFunc` + `maps.Keys` (1.23+) |
|
|
10
|
+
| `github.com/duo-labs/webauthn/protocol/util` for slice utils | `slices.Contains`, `slices.Index`, `slices.Equal` |
|
|
11
|
+
| Custom `Reverse(s)` | `slices.Reverse` |
|
|
12
|
+
| Manual `Map`/`Filter` loops | `slices.Collect`, `slices.DeleteFunc` |
|
|
13
|
+
| Slice prealloc patterns | `slices.Grow(s, n)` |
|
|
14
|
+
|
|
15
|
+
```go
|
|
16
|
+
import "slices"
|
|
17
|
+
|
|
18
|
+
s := []int{3, 1, 4, 1, 5}
|
|
19
|
+
slices.Sort(s) // sort.Ints replaced
|
|
20
|
+
ok := slices.Contains(s, 4) // manual loop replaced
|
|
21
|
+
idx := slices.IndexFunc(s, func(x int) bool { return x > 3 })
|
|
22
|
+
s = slices.DeleteFunc(s, func(x int) bool { return x == 1 })
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Maps
|
|
26
|
+
|
|
27
|
+
| Was: | Now: |
|
|
28
|
+
|------|------|
|
|
29
|
+
| Custom `MapKeys` | `slices.Collect(maps.Keys(m))` |
|
|
30
|
+
| Custom `MapValues` | `slices.Collect(maps.Values(m))` |
|
|
31
|
+
| `reflect.DeepEqual` for maps | `maps.Equal`, `maps.EqualFunc` |
|
|
32
|
+
| Manual map copy | `maps.Clone` |
|
|
33
|
+
|
|
34
|
+
```go
|
|
35
|
+
import "maps"
|
|
36
|
+
|
|
37
|
+
a := map[string]int{"a": 1, "b": 2}
|
|
38
|
+
b := maps.Clone(a)
|
|
39
|
+
maps.Equal(a, b) // true
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Iteration
|
|
43
|
+
|
|
44
|
+
| Was: | Now: |
|
|
45
|
+
|------|------|
|
|
46
|
+
| `for i := 0; i < n; i++` | `for i := range n` (1.22+) |
|
|
47
|
+
| Manual generator with channels | Range over functions: `for v := range gen` (1.23+) |
|
|
48
|
+
|
|
49
|
+
```go
|
|
50
|
+
// 1.22+
|
|
51
|
+
for i := range 10 {
|
|
52
|
+
fmt.Println(i)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 1.23+ — iterator function
|
|
56
|
+
func count(n int) func(yield func(int) bool) {
|
|
57
|
+
return func(yield func(int) bool) {
|
|
58
|
+
for i := 0; i < n; i++ { if !yield(i) { return } }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for v := range count(5) { fmt.Println(v) }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## HTTP routing
|
|
66
|
+
|
|
67
|
+
Pre-1.22: required `gorilla/mux`, `chi`, `httprouter`, etc. for method-aware routing.
|
|
68
|
+
|
|
69
|
+
Post-1.22: built-in.
|
|
70
|
+
|
|
71
|
+
```go
|
|
72
|
+
mux := http.NewServeMux()
|
|
73
|
+
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
|
|
74
|
+
id := r.PathValue("id")
|
|
75
|
+
// ...
|
|
76
|
+
})
|
|
77
|
+
mux.HandleFunc("POST /users", createUser)
|
|
78
|
+
mux.HandleFunc("DELETE /users/{id}", deleteUser)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
For complex needs (middleware chains, route groups, sub-router composition) `chi` is still excellent.
|
|
82
|
+
|
|
83
|
+
## Random
|
|
84
|
+
|
|
85
|
+
| Was: | Now: |
|
|
86
|
+
|------|------|
|
|
87
|
+
| `math/rand` + manual seed | `math/rand/v2` (auto-seeded, faster) |
|
|
88
|
+
|
|
89
|
+
```go
|
|
90
|
+
import "math/rand/v2"
|
|
91
|
+
|
|
92
|
+
n := rand.IntN(100) // [0, 100)
|
|
93
|
+
f := rand.Float64() // [0, 1)
|
|
94
|
+
shuffled := rand.Shuffle(...) // built-in
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Errors
|
|
98
|
+
|
|
99
|
+
| Was: | Now: |
|
|
100
|
+
|------|------|
|
|
101
|
+
| `pkg/errors` | `errors` package + `fmt.Errorf("%w", err)` |
|
|
102
|
+
| `pkg/errors.Wrap(err, "msg")` | `fmt.Errorf("msg: %w", err)` |
|
|
103
|
+
| `pkg/errors.Cause(err)` | `errors.Unwrap` / `errors.As` |
|
|
104
|
+
| Type-based error checks | `errors.Is`, `errors.As` |
|
|
105
|
+
|
|
106
|
+
```go
|
|
107
|
+
if errors.Is(err, sql.ErrNoRows) { /* not found */ }
|
|
108
|
+
|
|
109
|
+
var pgErr *pgconn.PgError
|
|
110
|
+
if errors.As(err, &pgErr) {
|
|
111
|
+
if pgErr.Code == "23505" { /* unique violation */ }
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Logging
|
|
116
|
+
|
|
117
|
+
| Was: | Now: |
|
|
118
|
+
|------|------|
|
|
119
|
+
| `github.com/sirupsen/logrus` (sometimes) | `log/slog` (structured) |
|
|
120
|
+
| `go-kit/log` | `log/slog` |
|
|
121
|
+
| Custom JSON logger | `slog.NewJSONHandler` |
|
|
122
|
+
|
|
123
|
+
```go
|
|
124
|
+
import "log/slog"
|
|
125
|
+
|
|
126
|
+
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
|
127
|
+
logger.Info("user signed in", "user_id", u.ID, "method", "oauth")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Compatible with most existing log shippers via JSON output.
|
|
131
|
+
|
|
132
|
+
## Crypto
|
|
133
|
+
|
|
134
|
+
`crypto/rand` for randomness; `crypto/subtle.ConstantTimeCompare` for secret comparison; `crypto/ed25519`, `crypto/aes`, etc. cover most needs without third-party libs.
|
|
135
|
+
|
|
136
|
+
### KDFs and hashes now in stdlib (Go 1.24)
|
|
137
|
+
|
|
138
|
+
Three `golang.org/x/crypto` packages graduated into the standard library — reach for these first:
|
|
139
|
+
|
|
140
|
+
| Was: `golang.org/x/crypto/...` | Now: stdlib |
|
|
141
|
+
|--------------------------------|-------------|
|
|
142
|
+
| `pbkdf2` | `crypto/pbkdf2` |
|
|
143
|
+
| `hkdf` | `crypto/hkdf` |
|
|
144
|
+
| `sha3` | `crypto/sha3` |
|
|
145
|
+
|
|
146
|
+
```go
|
|
147
|
+
import (
|
|
148
|
+
"crypto/pbkdf2"
|
|
149
|
+
"crypto/sha256"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
// Go 1.24: pbkdf2.Key returns (key, error) — always check the error
|
|
153
|
+
dk, err := pbkdf2.Key(sha256.New, password, salt, 600_000, 32)
|
|
154
|
+
if err != nil {
|
|
155
|
+
return err
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Still on `x/crypto` (no stdlib equivalent yet): `bcrypt`, `argon2`, `scrypt` for password hashing.
|
|
160
|
+
|
|
161
|
+
## Built-ins worth using
|
|
162
|
+
|
|
163
|
+
| Built-in | What |
|
|
164
|
+
|---------|------|
|
|
165
|
+
| `min(a, b, ...)` | Generic min (1.21+) |
|
|
166
|
+
| `max(a, b, ...)` | Generic max (1.21+) |
|
|
167
|
+
| `clear(m)` | Clear a map (1.21+) — keeps capacity, removes all entries |
|
|
168
|
+
| `any` | Alias for `interface{}` (1.18+) |
|
|
169
|
+
|
|
170
|
+
## Cases where you SHOULD keep the third-party
|
|
171
|
+
|
|
172
|
+
- **UUIDs** — `github.com/google/uuid` is still standard
|
|
173
|
+
- **HTTP client retry** — stdlib has none; `hashicorp/go-retryablehttp` works
|
|
174
|
+
- **Validation** — `go-playground/validator` covers struct tags well
|
|
175
|
+
- **Migrations** — `golang-migrate` or `goose` cover what stdlib does not
|
|
176
|
+
- **DI** — `uber/fx`, `google/wire`, `samber/do` (see `di-frameworks.md`)
|
|
177
|
+
|
|
178
|
+
## See also
|
|
179
|
+
|
|
180
|
+
- `modernize-guide.md` — running `modernize` to find these automatically
|
|
181
|
+
- `stay-updated.md` — when each stdlib addition arrived
|