Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 43 additions & 19 deletions cmd/optiqor/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,16 @@ func versionTemplate() string {

func newAnalyzeCmd() *cobra.Command {
var (
jsonOut bool
htmlPath string
offline bool
shareFlag bool
roast bool
minSev string
detectors []string
failOn string // severity threshold that triggers exit code 1
outputPath string
jsonOut bool
htmlPath string
offline bool
shareFlag bool
privateFlag bool
roast bool
minSev string
detectors []string
failOn string // severity threshold that triggers exit code 1
outputPath string
)
cmd := &cobra.Command{
Use: "analyze [chart]",
Expand All @@ -148,7 +149,8 @@ side-effect of parsing — they are not the headline feature.
optiqor analyze ./values.yaml --json
optiqor analyze ./chart --severity=med --fail-on=high
optiqor analyze ./chart --detector cpu-overprovisioned --detector missing-memory-limit
optiqor analyze ./my-chart --roast # same findings, snarkier titles`,
optiqor analyze ./my-chart --roast # same findings, snarkier titles
optiqor analyze ./my-chart --share --private # login-gated share link`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
path := "."
Expand All @@ -159,6 +161,11 @@ side-effect of parsing — they are not the headline feature.
if err != nil {
return err
}

if privateFlag && !shareFlag {
return fmt.Errorf("--private requires --share: pass both flags together")
}

rep, err := analyze.RunPath(abs)
if err != nil {
return err
Expand Down Expand Up @@ -196,7 +203,7 @@ side-effect of parsing — they are not the headline feature.
return err
}
if shareFlag {
emitShareURL(cmd, rep)
emitShareURL(cmd, rep, privateFlag)
}
_ = offline
return checkFailOn(rep, effFailOn)
Expand All @@ -206,6 +213,7 @@ side-effect of parsing — they are not the headline feature.
cmd.Flags().StringVar(&htmlPath, "html", "", "also write a self-contained HTML report to this path")
cmd.Flags().BoolVar(&offline, "offline", true, "do not perform any network calls (always true in Phase 1)")
cmd.Flags().BoolVar(&shareFlag, "share", false, "print optiqor.dev/r/<hash> for the sanitised analysis (no upload in Phase 1)")
cmd.Flags().BoolVar(&privateFlag, "private", false, "make the share link login-gated (requires --share; sends X-Optiqor-Private: 1)")
cmd.Flags().BoolVar(&roast, "roast", false, "humorous output (findings stay accurate)")
cmd.Flags().StringVar(&minSev, "severity", "", "drop findings below this severity (low|med|high)")
cmd.Flags().StringArrayVar(&detectors, "detector", nil, "only run findings from these detector IDs (repeatable)")
Expand Down Expand Up @@ -264,20 +272,36 @@ func openOutput(cmd *cobra.Command, path string) (io.Writer, func(), error) {
return f, func() { _ = f.Close() }, nil
}

// emitShareURL handles --share end-to-end. Prints to stderr so
// stdout (JSON/text) stays clean. Never blocks the success path — on
// upload failure we still print the URL so the user has a stable
// identifier to re-share later. OPTIQOR_SHARE_URL overrides the
// endpoint for self-hosted deploys.
func emitShareURL(cmd *cobra.Command, rep any) {
// emitShareURL handles the `--share` flag end-to-end.
//
// It computes the local content-addressable hash, attempts to upload
// the sanitised payload to the sandbox endpoint, and prints the
// resulting `optiqor.dev/r/<hash>` URL to stderr (so JSON/text output on
// stdout stays clean).
//
// When private is true the upload request carries X-Optiqor-Private: 1
// so the sandbox receiver returns a token-gated URL (Phase 2 backend
// work). The CLI side only sets the header; the receiver handles the
// rest.
//
// The function never blocks the caller's success path — if the upload
// fails (offline, sandbox down, 5xx), we still print the URL so the
// user has a stable identifier they can re-share later. The endpoint
// is overridable via OPTIQOR_SHARE_URL for self-hosted deploys.

func emitShareURL(cmd *cobra.Command, rep any, private bool) {
endpoint := os.Getenv("OPTIQOR_SHARE_URL")
res := share.Upload(rep, endpoint)
res := share.Upload(rep, endpoint, private)
if res.Hash == "" {
return
}
suffix := ""
if res.Posted {
suffix = " (uploaded)"
if private {
suffix = " (uploaded · login-gated)"
} else {
suffix = " (uploaded)"
}
} else if res.Error != "" {
suffix = " (offline / not uploaded — " + res.Error + ")"
}
Expand Down
20 changes: 16 additions & 4 deletions internal/share/share.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,19 @@ type UploadResult struct {
Error string
}

// Upload POSTs the sanitised report JSON. Hard rule: the only
// outbound call the CLI makes, and only on --share. Never retries,
// never logs bodies, never sends anything but the sanitised payload.
func Upload(report any, endpoint string) UploadResult {
// Upload attempts to POST the sanitised report JSON to endpoint and
// returns the resulting (hash, URL, posted) tuple.
//
// When private is true, the request carries X-Optiqor-Private: 1 so
// the sandbox receiver returns a token-gated URL (Phase 2 backend work).
// The CLI side only sets the header; the token arrives in the response
// body once the receiver is live.
//
// CLI hard rule: this is the only outbound network call optiqor makes,
// and only when the user explicitly passes --share. The function never
// retries; never logs request bodies; never sends anything but the
// sanitised payload.
func Upload(report any, endpoint string, private bool) UploadResult {
hash, err := Hash(report)
if err != nil {
return UploadResult{Error: err.Error()}
Expand Down Expand Up @@ -142,6 +151,9 @@ func Upload(report any, endpoint string) UploadResult {
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Optiqor-Hash", hash)
req.Header.Set("User-Agent", "optiqor-cli")
if private {
req.Header.Set("X-Optiqor-Private", "1")
}

client := &http.Client{Timeout: uploadTimeout}
resp, err := client.Do(req)
Expand Down
33 changes: 32 additions & 1 deletion internal/share/share_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func TestUpload(t *testing.T) {
defer srv.Close()
}

res := Upload(tc.input, srv.URL)
res := Upload(tc.input, srv.URL, false)

if res.Posted != tc.wantPost {
t.Fatalf("Posted = %v want %v; Error=%q", res.Posted, tc.wantPost, res.Error)
Expand Down Expand Up @@ -216,3 +216,34 @@ func TestIsHash(t *testing.T) {
})
}
}

func TestUpload_PrivateHeaderSent(t *testing.T) {
var gotPrivate string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotPrivate = r.Header.Get("X-Optiqor-Private")
w.WriteHeader(http.StatusAccepted)
}))
defer srv.Close()

res := Upload(sample{Workloads: 1}, srv.URL, true)
if !res.Posted {
t.Fatalf("Posted = false, error = %q", res.Error)
}
if gotPrivate != "1" {
t.Errorf("X-Optiqor-Private = %q, want %q", gotPrivate, "1")
}
}

func TestUpload_NoPrivateHeaderByDefault(t *testing.T) {
var gotPrivate string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotPrivate = r.Header.Get("X-Optiqor-Private")
w.WriteHeader(http.StatusAccepted)
}))
defer srv.Close()

Upload(sample{Workloads: 1}, srv.URL, false)
if gotPrivate != "" {
t.Errorf("X-Optiqor-Private should be absent, got %q", gotPrivate)
}
}