Fix missing __typename checks and standalone var declarations

Add __typename to GraphQL queries and type guards in risk view,
risk list, user view, and user list commands to prevent silent
garbage output when a wrong node type ID is passed. Wrap
standalone var declarations in var () blocks per style guide.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-03-15 12:24:39 +01:00
parent 0438b8457d
commit 779f549530
7 changed files with 49 additions and 15 deletions

View File

@@ -24,7 +24,9 @@ import (
"go.probo.inc/probo/pkg/cmd/root" "go.probo.inc/probo/pkg/cmd/root"
) )
var version string = "unknown" var (
version string = "unknown"
)
func main() { func main() {
ios := iostreams.System() ios := iostreams.System()

View File

@@ -44,13 +44,15 @@ type (
} }
) )
var ValidKeys = []string{ var (
"editor", ValidKeys = []string{
"browser", "editor",
"pager", "browser",
"prompt", "pager",
"http_timeout", "prompt",
} "http_timeout",
}
)
func (c *Config) Get(key string) (string, error) { func (c *Config) Get(key string) (string, error) {
switch key { switch key {

View File

@@ -26,10 +26,12 @@ import (
"go.probo.inc/probo/pkg/cmd/cmdutil" "go.probo.inc/probo/pkg/cmd/cmdutil"
) )
var schemaEndpoints = map[string]string{ var (
"console": "/api/console/v1/graphql", schemaEndpoints = map[string]string{
"connect": "/api/connect/v1/graphql", "console": "/api/console/v1/graphql",
} "connect": "/api/connect/v1/graphql",
}
)
func NewCmdAPI(f *cmdutil.Factory) *cobra.Command { func NewCmdAPI(f *cmdutil.Factory) *cobra.Command {
var ( var (

View File

@@ -26,6 +26,7 @@ import (
const listQuery = ` const listQuery = `
query($id: ID!, $first: Int, $after: CursorKey, $orderBy: RiskOrder, $filter: RiskFilter) { query($id: ID!, $first: Int, $after: CursorKey, $orderBy: RiskOrder, $filter: RiskFilter) {
node(id: $id) { node(id: $id) {
__typename
... on Organization { ... on Organization {
risks(first: $first, after: $after, orderBy: $orderBy, filter: $filter) { risks(first: $first, after: $after, orderBy: $orderBy, filter: $filter) {
totalCount totalCount
@@ -138,13 +139,20 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
flagLimit, flagLimit,
func(data json.RawMessage) (*api.Connection[risk], error) { func(data json.RawMessage) (*api.Connection[risk], error) {
var resp struct { var resp struct {
Node struct { Node *struct {
Risks api.Connection[risk] `json:"risks"` Typename string `json:"__typename"`
Risks api.Connection[risk] `json:"risks"`
} `json:"node"` } `json:"node"`
} }
if err := json.Unmarshal(data, &resp); err != nil { if err := json.Unmarshal(data, &resp); err != nil {
return nil, err return nil, err
} }
if resp.Node == nil {
return nil, fmt.Errorf("organization %s not found", flagOrg)
}
if resp.Node.Typename != "Organization" {
return nil, fmt.Errorf("expected Organization node, got %s", resp.Node.Typename)
}
return &resp.Node.Risks, nil return &resp.Node.Risks, nil
}, },
) )

View File

@@ -27,6 +27,7 @@ import (
const viewQuery = ` const viewQuery = `
query($id: ID!) { query($id: ID!) {
node(id: $id) { node(id: $id) {
__typename
... on Risk { ... on Risk {
id id
name name
@@ -49,6 +50,7 @@ query($id: ID!) {
type viewResponse struct { type viewResponse struct {
Node *struct { Node *struct {
Typename string `json:"__typename"`
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description *string `json:"description"` Description *string `json:"description"`
@@ -112,6 +114,10 @@ func NewCmdView(f *cmdutil.Factory) *cobra.Command {
return fmt.Errorf("risk %s not found", args[0]) return fmt.Errorf("risk %s not found", args[0])
} }
if resp.Node.Typename != "Risk" {
return fmt.Errorf("expected Risk node, got %s", resp.Node.Typename)
}
if *flagOutput == cmdutil.OutputJSON { if *flagOutput == cmdutil.OutputJSON {
return cmdutil.PrintJSON(f.IOStreams.Out, resp.Node) return cmdutil.PrintJSON(f.IOStreams.Out, resp.Node)
} }

View File

@@ -26,6 +26,7 @@ import (
const listQuery = ` const listQuery = `
query($id: ID!, $first: Int, $after: CursorKey, $orderBy: ProfileOrder, $filter: ProfileFilter) { query($id: ID!, $first: Int, $after: CursorKey, $orderBy: ProfileOrder, $filter: ProfileFilter) {
node(id: $id) { node(id: $id) {
__typename
... on Organization { ... on Organization {
profiles(first: $first, after: $after, orderBy: $orderBy, filter: $filter) { profiles(first: $first, after: $after, orderBy: $orderBy, filter: $filter) {
totalCount totalCount
@@ -135,13 +136,20 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
flagLimit, flagLimit,
func(data json.RawMessage) (*api.Connection[profile], error) { func(data json.RawMessage) (*api.Connection[profile], error) {
var resp struct { var resp struct {
Node struct { Node *struct {
Typename string `json:"__typename"`
Profiles api.Connection[profile] `json:"profiles"` Profiles api.Connection[profile] `json:"profiles"`
} `json:"node"` } `json:"node"`
} }
if err := json.Unmarshal(data, &resp); err != nil { if err := json.Unmarshal(data, &resp); err != nil {
return nil, err return nil, err
} }
if resp.Node == nil {
return nil, fmt.Errorf("organization %s not found", flagOrg)
}
if resp.Node.Typename != "Organization" {
return nil, fmt.Errorf("expected Organization node, got %s", resp.Node.Typename)
}
return &resp.Node.Profiles, nil return &resp.Node.Profiles, nil
}, },
) )

View File

@@ -27,6 +27,7 @@ import (
const viewQuery = ` const viewQuery = `
query($id: ID!) { query($id: ID!) {
node(id: $id) { node(id: $id) {
__typename
... on Profile { ... on Profile {
id id
fullName fullName
@@ -46,6 +47,7 @@ query($id: ID!) {
type viewResponse struct { type viewResponse struct {
Node *struct { Node *struct {
Typename string `json:"__typename"`
ID string `json:"id"` ID string `json:"id"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
EmailAddress string `json:"emailAddress"` EmailAddress string `json:"emailAddress"`
@@ -106,6 +108,10 @@ func NewCmdView(f *cmdutil.Factory) *cobra.Command {
return fmt.Errorf("user %s not found", args[0]) return fmt.Errorf("user %s not found", args[0])
} }
if resp.Node.Typename != "Profile" {
return fmt.Errorf("expected Profile node, got %s", resp.Node.Typename)
}
if *flagOutput == cmdutil.OutputJSON { if *flagOutput == cmdutil.OutputJSON {
return cmdutil.PrintJSON(f.IOStreams.Out, resp.Node) return cmdutil.PrintJSON(f.IOStreams.Out, resp.Node)
} }