feat(mint): add --pem-dir to bootstrap PEMs during deploy - #1690
Conversation
Site previewPreview: https://dd3901ea-site.fullsend-ai.workers.dev Commit: |
ReviewFindingsHigh
Low
Previous runReviewFindingsHigh
Low
Previous run (2)ReviewFindingsHigh
Low
Previous runReviewFindingsHigh
Low
Previous run (2)ReviewFindingsHigh
Low
Previous run (3)ReviewFindingsHigh
Low
|
| resp, err := http.DefaultClient.Do(req) | ||
| if err != nil { | ||
| return 0, fmt.Errorf("looking up app %s: %w", slug, err) | ||
| } |
There was a problem hiding this comment.
[low] correctness
lookupAppID uses http.DefaultClient which has no timeout. A hung connection to the GitHub API could block indefinitely if no context deadline is set by the caller.
Suggested fix: Replace http.DefaultClient with a client that has a Timeout field set (e.g., 30s), or document that callers must set a context deadline.
|
|
||
| roles := defaultMintRoles() | ||
| agentPEMs := make(map[string][]byte, len(roles)) | ||
| agentAppIDs := make(map[string]string, len(roles)) |
There was a problem hiding this comment.
[low] correctness
loadAppSetPEMs reads PEM files but does not validate that the file contents are actually PEM-encoded. A non-PEM file would be silently accepted and only fail later during cryptographic operations.
Suggested fix: Add a pem.Decode check after reading the file to verify at least one PEM block is present.
f8d4050 to
76f1d98
Compare
Fresh mint deploy creates infrastructure but no PEM secrets, so mint enroll fails until admin install runs. Adding --pem-dir lets operators provide PEM files at deploy time — the CLI auto-discovers app IDs from the GitHub API and stores everything in Secret Manager. After deploying with PEMs, mint enroll works immediately without needing admin install first. Signed-off-by: Wayne Sun <gsun@redhat.com>
The test file uses fake PEM keys for testing the --pem-dir flag. Signed-off-by: Wayne Sun <gsun@redhat.com>
Add deploy-time safeguards for --pem-dir: - Validate PEM format via appsetup.ValidateRSAPEM before storing - Verify each PEM matches its GitHub App via JWT-authenticated GET /app - Add directory existence check and pre-flight file existence check - Show expected vs found PEM filenames in error messages - Handle GitHub API rate limits (403/429) with clear guidance - Use dedicated HTTP client with 30s timeout instead of DefaultClient - Drain response bodies to enable connection reuse - Clarify --pem-dir is optional (first-time bootstrap only) in docs Signed-off-by: Wayne Sun <gsun@redhat.com>
76f1d98 to
e7011d0
Compare
| @@ -102,6 +305,36 @@ func newMintDeployCmd() *cobra.Command { | |||
| if skipDeploy { | |||
There was a problem hiding this comment.
[low] correctness
The dry-run branch duplicates PEM directory validation logic (stat dir, check role files exist, validate each PEM) that loadAppSetPEMs already implements. Drift risk if validation rules change.
Suggested fix: Extract shared PEM directory validation into a helper function called by both the dry-run and real code paths.
| @@ -41,6 +52,193 @@ func resolveRole(role string) string { | |||
| return role | |||
| } | |||
|
|
|||
There was a problem hiding this comment.
[low] style
githubAPIBaseURL and githubHTTPClient are package-level mutable globals swapped via save/restore in tests. Fragile if tests ever run in parallel.
Suggested fix: Consider passing HTTP client and base URL via a struct or function parameters instead of package-level vars.
…tion - Extract ~30 lines of filesystem validation (dir exists, is dir, role PEM files exist, PEM format valid) into validatePEMDir, shared by both dry-run and loadAppSetPEMs paths - Add defense-in-depth: verifyPEMMatchesApp now decodes the GET /app response body and verifies the returned app ID matches expectations - Add dry-run message noting API verification is skipped - Add comment about GitHubOrgs/app-set name coupling - Add test parallelism guard comment Signed-off-by: Wayne Sun <waynesun09@gmail.com> Signed-off-by: Wayne Sun <gsun@redhat.com>
Covers the new defense-in-depth branch where GET /app returns 200 but with a different app ID than expected. Signed-off-by: Wayne Sun <waynesun09@gmail.com> Signed-off-by: Wayne Sun <gsun@redhat.com>
| - id: trailing-whitespace | ||
| - id: detect-private-key | ||
| exclude: "internal/layers/secrets_test\\.go$|internal/security/scanner_test\\.go$|internal/dispatch/gcf/provisioner_test\\.go$|tests/.*test_.*\\.py$" | ||
| exclude: "internal/layers/secrets_test\\.go$|internal/security/scanner_test\\.go$|internal/dispatch/gcf/provisioner_test\\.go$|internal/cli/mint_test\\.go$|tests/.*test_.*\\.py$" |
There was a problem hiding this comment.
[high] protected-path
Protected infrastructure file modified without a linked issue. The change adds internal/cli/mint_test.go$ to the detect-private-key exclusion list, justified by fake PEM test fixtures. Human reviewers must verify protected-path changes regardless, and the absence of a linked issue means there is no traceable authorization.
Suggested fix: Link a tracking issue that authorizes the .pre-commit-config.yaml change, or have a human reviewer explicitly approve this protected-path modification.
| @@ -41,6 +52,216 @@ func resolveRole(role string) string { | |||
| return role | |||
| } | |||
|
|
|||
There was a problem hiding this comment.
[low] style
githubAPIBaseURL and githubHTTPClient are package-level mutable globals swapped via save/restore in tests. Fragile if tests ever run in parallel.
Suggested fix: Consider passing an HTTP client and base URL via a struct or function parameters instead of package-level vars.
Summary
--pem-dirflag tomint deploythat reads{role}.pemfiles from disk, auto-discovers app IDs via unauthenticatedGET /apps/{slug}, and stores PEMs in Secret Manager during deployment--pem-dir,mint enroll <org>works immediately without needingadmin installfirstfullsend-aiapp set; custom app sets continue to useadmin installDepends on
--source-orgto--app-setin enroll) — no code conflicts, but should merge first for consistent namingTest plan
go vet ./internal/cli/...cleango test ./internal/cli/— all pass (including newTestLookupAppID_*,TestLoadAppSetPEMs_*,TestMintDeployCmd_PemDirFlag,TestMintDeployCmd_DryRunWithPemDir)go build ./cmd/fullsend/compilesmake lintcleanmint deploy --project=<PROJECT> --pem-dir=/path/to/pems --dry-runshows bootstrap planmint enroll <org>succeeds withoutadmin install