Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/cgo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ jobs:
# Enforce selected production custom analyzers without blocking on unrelated
# legacy custom analyzer findings in tests or other analyzer families.
- name: Run custom linters
run: make golint-custom LINTER_FLAGS="-errstringmatch -panicinlibrarycode -manualmutexunlock -osexitinlibrary -rawloginlib -regexpcompileinfunction -fprintlnsprintf -strconvparseignorederror -test=false"
run: make golint-custom LINTER_FLAGS="-errstringmatch -panicinlibrarycode -manualmutexunlock -osexitinlibrary -rawloginlib -regexpcompileinfunction -fprintlnsprintf -strconvparseignorederror -jsonmarshalignoredeerror -test=false"

actions-build:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/daily-model-inventory.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/cli/mcp_config_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ func ensureMCPConfig(verbose bool) error {

// Check if the server is already configured correctly
if existingConfig, exists := config.MCPServers[ghAwServerName]; exists {
existingJSON, _ := json.Marshal(existingConfig)
newJSON, _ := json.Marshal(ghAwConfig)
existingJSON, _ := json.Marshal(existingConfig) //nolint:jsonmarshalignoredeerror // VSCodeMCPServer contains only JSON-safe types (string, []string)
newJSON, _ := json.Marshal(ghAwConfig) //nolint:jsonmarshalignoredeerror // VSCodeMCPServer contains only JSON-safe types (string, []string)
if string(existingJSON) == string(newJSON) {
mcpConfigLog.Print("Configuration is identical, skipping")
if verbose {
Expand Down
11 changes: 11 additions & 0 deletions pkg/linters/jsonmarshalignoredeerror/jsonmarshalignoredeerror.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"

"github.com/github/gh-aw/pkg/linters/internal/nolint"
)

// Analyzer is the json-marshal-ignored-error analysis pass.
Expand All @@ -22,6 +24,7 @@ var Analyzer = &analysis.Analyzer{

func run(pass *analysis.Pass) (any, error) {
insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
noLintLinesByFile := nolint.BuildLineIndex(pass, "jsonmarshalignoredeerror")
nodeFilter := []ast.Node{(*ast.AssignStmt)(nil)}
insp.Preorder(nodeFilter, func(n ast.Node) {
assign, ok := n.(*ast.AssignStmt)
Expand All @@ -36,6 +39,10 @@ func run(pass *analysis.Pass) (any, error) {
call, ok := assign.Rhs[0].(*ast.CallExpr)
if ok {
if isJSONFunc(pass, call, "Marshal") {
position := pass.Fset.PositionFor(call.Pos(), false)
if nolint.HasDirective(position, noLintLinesByFile) {
return
}
pass.ReportRangef(call, "error return from json.Marshal is discarded; marshal failures produce nil bytes silently")
}
}
Expand All @@ -49,6 +56,10 @@ func run(pass *analysis.Pass) (any, error) {
call, ok := assign.Rhs[0].(*ast.CallExpr)
if ok {
if isJSONFunc(pass, call, "Unmarshal") {
position := pass.Fset.PositionFor(call.Pos(), false)
if nolint.HasDirective(position, noLintLinesByFile) {
return
}
Comment on lines +59 to +62
pass.ReportRangef(call, "error return from json.Unmarshal is discarded; unmarshal failures leave the target value in a partial state")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,9 @@ func Good() error {
}
return nil
}

func Suppressed() {
f := Foo{X: 1}
val, _ := json.Marshal(f) //nolint:jsonmarshalignoredeerror // Foo contains only JSON-safe types
_ = val
}
2 changes: 1 addition & 1 deletion pkg/workflow/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func writeArgsToYAML(yaml *strings.Builder, args []string, indent string) {
for _, arg := range args {
yaml.WriteString(",\n")
// Use json.Marshal to properly quote and escape the argument
quotedArg, _ := json.Marshal(arg)
quotedArg, _ := json.Marshal(arg) //nolint:jsonmarshalignoredeerror // marshaling a string cannot fail
yaml.WriteString(indent + string(quotedArg))
}
}
4 changes: 2 additions & 2 deletions pkg/workflow/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ func generateCacheMemoryValidation(builder *strings.Builder, data *WorkflowData)
cacheDir := cacheMemoryDirFor(cache.ID)

// Prepare allowed extensions array for JavaScript
allowedExtsJSON, _ := json.Marshal(cache.AllowedExtensions)
allowedExtsJSON, _ := json.Marshal(cache.AllowedExtensions) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail

// Build validation script
var validationScript strings.Builder
Expand Down Expand Up @@ -914,7 +914,7 @@ func (c *Compiler) buildUpdateCacheMemoryJob(data *WorkflowData, threatDetection
cacheLog.Printf("Skipping validation step for cache %s in update job (empty allowed-extensions means all files are allowed)", cache.ID)
} else {
// Prepare allowed extensions array for JavaScript
allowedExtsJSON, _ := json.Marshal(cache.AllowedExtensions)
allowedExtsJSON, _ := json.Marshal(cache.AllowedExtensions) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail

// Build validation script
var validationScript strings.Builder
Expand Down
6 changes: 3 additions & 3 deletions pkg/workflow/compiler_experiments.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,17 +448,17 @@ func buildExperimentSpecJSON(experiments map[string][]string, configs map[string
if i > 0 {
sb.WriteString(",")
}
keyBytes, _ := json.Marshal(name)
keyBytes, _ := json.Marshal(name) //nolint:jsonmarshalignoredeerror // marshaling a string cannot fail
sb.Write(keyBytes)
sb.WriteString(":")

// Use the full config when available so the JS can consume metadata.
if cfg, ok := configs[name]; ok && cfg != nil {
cfgBytes, _ := json.Marshal(cfg)
cfgBytes, _ := json.Marshal(cfg) //nolint:jsonmarshalignoredeerror // ExperimentConfig contains only JSON-safe types (strings, ints, []string)
sb.Write(cfgBytes)
} else {
// Fallback: bare variants array (legacy behaviour).
varBytes, _ := json.Marshal(experiments[name])
varBytes, _ := json.Marshal(experiments[name]) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail
sb.Write(varBytes)
}
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/workflow/compiler_pre_activation_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,11 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec
if len(data.SkipIfCheckFailing.Include) > 0 || len(data.SkipIfCheckFailing.Exclude) > 0 || data.SkipIfCheckFailing.Branch != "" || data.SkipIfCheckFailing.AllowPending {
steps = append(steps, " env:\n")
if len(data.SkipIfCheckFailing.Include) > 0 {
includeJSON, _ := json.Marshal(data.SkipIfCheckFailing.Include)
includeJSON, _ := json.Marshal(data.SkipIfCheckFailing.Include) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail
steps = append(steps, fmt.Sprintf(" GH_AW_SKIP_CHECK_INCLUDE: %q\n", string(includeJSON)))
}
if len(data.SkipIfCheckFailing.Exclude) > 0 {
excludeJSON, _ := json.Marshal(data.SkipIfCheckFailing.Exclude)
excludeJSON, _ := json.Marshal(data.SkipIfCheckFailing.Exclude) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail
steps = append(steps, fmt.Sprintf(" GH_AW_SKIP_CHECK_EXCLUDE: %q\n", string(excludeJSON)))
}
if data.SkipIfCheckFailing.Branch != "" {
Expand Down Expand Up @@ -227,7 +227,7 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec
steps = append(steps, fmt.Sprintf(" uses: %s\n", getCachedActionPin("actions/github-script", data)))
steps = append(steps, " env:\n")
// Pass commands as JSON array
commandsJSON, _ := json.Marshal(data.Command)
commandsJSON, _ := json.Marshal(data.Command) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail
steps = append(steps, fmt.Sprintf(" GH_AW_COMMANDS: %q\n", string(commandsJSON)))
if data.CommandPlaceholder != "" {
steps = append(steps, fmt.Sprintf(" GH_AW_COMMAND_PLACEHOLDER: %q\n", data.CommandPlaceholder))
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/compiler_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ func (c *Compiler) generateCreateAwInfo(yaml *strings.Builder, data *WorkflowDat
// Allowed domains as JSON array string
domainsJSON := "[]"
if len(allowedDomains) > 0 {
b, _ := json.Marshal(allowedDomains)
b, _ := json.Marshal(allowedDomains) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail
domainsJSON = string(b)
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/copilot_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (e *CopilotEngine) parseSessionJSONL(logContent string, verbose bool) (LogM
// Calculate input size
inputSize := 0
if content.Input != nil {
inputJSON, _ := json.Marshal(content.Input)
inputJSON, _ := json.Marshal(content.Input) //nolint:jsonmarshalignoredeerror // used only for len() size metric; failure yields len(nil)==0 which is acceptable
inputSize = len(inputJSON)
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/workflow/mcp_config_playwright_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,21 +70,21 @@ var mcpPlaywrightLog = logger.New("workflow:mcp_config_playwright_renderer")
// If inline is true, it writes on a single line; otherwise uses multi-line formatting.
// Always appends a trailing comma after the closing bracket.
func writeJSONStringArray(b *strings.Builder, indent, key string, values []string, inline bool) {
jsonKey, _ := json.Marshal(key)
jsonKey, _ := json.Marshal(key) //nolint:jsonmarshalignoredeerror // marshaling a string cannot fail
if inline {
b.WriteString(indent + string(jsonKey) + ": [")
for i, v := range values {
if i > 0 {
b.WriteString(", ")
}
jsonVal, _ := json.Marshal(v)
jsonVal, _ := json.Marshal(v) //nolint:jsonmarshalignoredeerror // marshaling a string cannot fail
b.Write(jsonVal)
}
b.WriteString("],\n")
} else {
b.WriteString(indent + string(jsonKey) + ": [\n")
for i, v := range values {
jsonVal, _ := json.Marshal(v)
jsonVal, _ := json.Marshal(v) //nolint:jsonmarshalignoredeerror // marshaling a string cannot fail
b.WriteString(indent + " " + string(jsonVal))
if i < len(values)-1 {
b.WriteString(",")
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/mcp_renderer_github.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ func RenderGitHubMCPDockerConfig(yaml *strings.Builder, options GitHubMCPDockerO
if len(options.CustomArgs) > 0 {
yaml.WriteString(" \"args\": [\n")
for _, arg := range options.CustomArgs {
quotedArg, _ := json.Marshal(arg)
quotedArg, _ := json.Marshal(arg) //nolint:jsonmarshalignoredeerror // marshaling a string cannot fail
yaml.WriteString(" " + string(quotedArg) + ",\n")
}
yaml.WriteString(" ],\n")
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/repo_memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ func (c *Compiler) buildPushRepoMemoryJob(data *WorkflowData, threatDetectionEna
fmt.Fprintf(&step, " MAX_FILE_COUNT: %d\n", memory.MaxFileCount)
fmt.Fprintf(&step, " MAX_PATCH_SIZE: %d\n", memory.MaxPatchSize)
// Pass allowed extensions as JSON array
allowedExtsJSON, _ := json.Marshal(memory.AllowedExtensions)
allowedExtsJSON, _ := json.Marshal(memory.AllowedExtensions) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail
fmt.Fprintf(&step, " ALLOWED_EXTENSIONS: '%s'\n", allowedExtsJSON)
if fileGlobFilter != "" {
// Quote the value to prevent YAML alias interpretation of patterns like *.md
Expand Down
5 changes: 4 additions & 1 deletion pkg/workflow/safe_outputs_config_generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,10 @@ func generateSafeOutputsConfig(data *WorkflowData) (string, error) {
if len(safeOutputsConfig) == 0 {
return "", nil
}
configJSON, _ := json.Marshal(safeOutputsConfig)
configJSON, err := json.Marshal(safeOutputsConfig)
if err != nil {
return "", fmt.Errorf("marshaling safe-outputs config: %w", err)
}
safeOutputsConfigLog.Printf("Safe outputs config generation complete: %d tool types configured", len(safeOutputsConfig))
return string(configJSON), nil
}
Expand Down