Skip to content

generatedyamlheredoc: bare (( ... )) arithmetic with << is misreported as a heredoc #53901

Description

@github-actions

Summary

generatedyamlheredoc (pkg/linters/generatedyamlheredoc/generatedyamlheredoc.go) flags shell heredocs embedded in generated workflow YAML, but its arithmetic-context guard only recognizes the $(( spelling of shell arithmetic expansion. Bare (( ... )) arithmetic commands/conditions are not recognized, so a << bit-shift inside one is misreported as a heredoc.

Evidence

lineContainsShellHeredoc (generatedyamlheredoc.go:89) special-cases arithmetic via:

if strings.LastIndex(line[:operatorIndex], "$((") > strings.LastIndex(line[:operatorIndex], "))") {
    line = line[afterIndex:]
    continue
}

This only looks for the literal substring "$((". Bash also supports the arithmetic compound command/conditional (( expr )) (no leading $), e.g. (( count << 1 )), if (( retries << 1 > max )); then. For that spelling, line[:operatorIndex] contains "((" but never "$((", so the guard is skipped entirely.

The code then falls through to the delimiter check:

afterOperator := strings.TrimLeft(line[afterIndex:], " \t")
delimiterStart := afterOperator[0]
return delimiterStart == '-' || delimiterStart == '\'' || delimiterStart == '"' || isShellWordByte(delimiterStart)

isShellWordByte accepts 0-9, so any numeric right-hand operand (<< 1, << 2, ...) satisfies delimiterStart, and the line is reported as a heredoc.

The existing test suite only covers the $(( spelling: "echo $((1 << 2))\n" in testdata/src/generatedyamlheredoc/generatedyamlheredoc.go:13 is correctly not flagged. There is no test case for bare (( ... << ... )), so this gap is untested and unguarded.

Reproduction

Add this string to the testdata fixture and run the analyzer:

"(( count << 1 ))\n",

Expected: not flagged (it's arithmetic, not a heredoc). Actual: flagged with "generated workflow shell uses a heredoc; pass plain content through an environment variable to a JavaScript renderer instead (do not base64 encode it)" -- a false positive, since there is no heredoc here at all.

Impact

Currently latent: a repo-wide search of pkg/**/*.go (non-test) found no production string literal combining bare (( arithmetic with <<, and generatedyamlheredoc is not wired into any CI LINTER_FLAGS enforcement list, so nothing breaks today. But the linter targets generated-workflow shell script strings, which are exactly where arithmetic idioms like (( i << 1 )) or retry-backoff/bitmask logic are likely to show up as the generator grows -- the first such addition would produce a confusing, incorrect lint failure with no legitimate fix available (there's no heredoc to remove).

Suggested fix

Broaden the arithmetic guard to also match bare ((, not just $(( -- e.g. search for "((" instead of "$((" in the "are we inside an open arithmetic expression" check (or check both spellings). Add a testdata case for "(( count << 1 ))\n" (and ideally "if (( a << 2 )); then\n") alongside the existing $((1 << 2)) case to lock in the fix.

Location

  • pkg/linters/generatedyamlheredoc/generatedyamlheredoc.go:89 (the $(( guard)
  • pkg/linters/generatedyamlheredoc/testdata/src/generatedyamlheredoc/generatedyamlheredoc.go:13 (existing $(( test case, needs a bare-(( sibling)

Generated by 🤖 Sergo - Serena Go Expert · agent · 131.1 AIC · ⌖ 4.82 AIC · ⊞ 6.3K ·

  • expires on Aug 25, 2026, 8:06 PM UTC-08:00

Metadata

Metadata

Labels

cookieIssue Monster Loves Cookies!sergo

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions