Skip to content

Conversation

@Frzk
Copy link

@Frzk Frzk commented Feb 2, 2026

Related to #147

schneems and others added 6 commits January 26, 2026 08:34
* Update Node version to 24.x LTS

Node 24 [has been LTS since October 2025](https://nodejs.org/en/blog/release/v24.11.0)

Signed-off-by: Stefan Wrobel <swrobel@users.noreply.github.com>

* Changelog

* Apply suggestions from code review

Co-authored-by: Stefan Wrobel <swrobel@users.noreply.github.com>
Signed-off-by: Richard Schneeman <richard.schneeman+no-recruiters@gmail.com>

---------

Signed-off-by: Stefan Wrobel <swrobel@users.noreply.github.com>
Signed-off-by: Richard Schneeman <richard.schneeman+no-recruiters@gmail.com>
Co-authored-by: Stefan Wrobel <swrobel@users.noreply.github.com>
…et (heroku#1700)

* Set WEB_CONCURRENCY_SET_BY=heroku/ruby if WEB_CONCURRENCY gets auto-set

The Ruby buildpack sets the `WEB_CONCURRENCY` env var at app boot based on the
current dyno size if the (deprecated) `SENSIBLE_DEFAULTS` mode is enabled, so
long as the user hasn't specified a custom value via the app's config vars.

Now, in addition to setting the `WEB_CONCURRENCY` env var, the buildpack will
also set `WEB_CONCURRENCY_SET_BY=heroku/ruby` to allow the app, other boot time
scripts, or humans more easily differentiate between a user and a buildpack
provided `WEB_CONCURRENCY` (and determine which buildpack actually set it).

In addition to assisting with debugging, this allows for UX improvements in
other buildpacks such as the PHP buildpack, which can now check for
`WEB_CONCURRENCY_SET_BY` in its boot time Apache/Nginx `heroku-php-...` scripts
to ignore `WEB_CONCURRENCY` in cases where users have ordered the buildpacks on
their app in the wrong order (the "primary" language is supposed to be listed
last, otherwise the wrong concurrency value will be used).

See also:
heroku/heroku-buildpack-nodejs#932
heroku/heroku-buildpack-python#2015

GUS-W-20882005

* Track SENSIBLE_DEFAULTS usage

This feature is deprecated, let's see how many people still rely on it.

---------

Signed-off-by: Richard Schneeman <rschneeman@salesforce.com>
Co-authored-by: Richard Schneeman <rschneeman@salesforce.com>
* Add standardrb gem for Ruby linting

* Add standardrb to CI lint job

* Auto-fix standardrb violations

* Add .standard.yml to exclude test fixture repos from linting

The repos/ directory contains external Rails application fixtures used for
integration testing. These should not be linted as they represent external
code, not the buildpack's own code.

* Fix Style/StringLiterals in Gemfile

Lint failure: Style/StringLiterals - Prefer double-quoted strings unless you
need single quotes to avoid extra backslashes for escaping.

The fix uses double quotes consistently for all gem declarations, matching
the style used by the rest of the Gemfile.

* Disable lint rules for intentional patterns in bin/support scripts

Lint failures disabled:
- Lint/RescueException: Rescuing Exception is intentional in buildpack entry
  points to ensure all errors (including SignalException, SystemExit) are
  properly reported to users rather than causing cryptic failures.
- Style/MixinUsage: Including modules at top level is intentional for these
  standalone scripts, which are not library code and benefit from direct
  access to shell helper methods.

* Fix lint violations in heroku_build_report.rb

Lint failures:
- Lint/NonLocalExitFromIterator: Changed 'return' to 'next' in the each block.
  Using 'return' would exit the entire capture method, while 'next' correctly
  skips to the next iteration.
- Style/RedundantInterpolation: Changed "\#{key}" to key.to_s since the key
  is already a string after strip. This is more explicit and avoids
  unnecessary string interpolation.

* Fix lint violations in language_pack.rb

Lint failures:
- Lint/AssignmentInCondition: Wrapped 'pack = LanguagePack.detect(...)' in
  parentheses to clarify that the assignment is intentional.
- Style/SafeNavigation: Changed 'if pack_klass; pack_klass.new(...); end'
  to 'pack_klass&.new(...)' using safe navigation operator. This is more
  idiomatic Ruby and clearly expresses the intent to call new only if
  pack_klass is not nil.

* Fix Performance/UnfreezeString violations

Lint failure: Performance/UnfreezeString - Use unary plus to get an unfrozen
string literal.

Changed String.new("") and String.new("...") to +"" and +"..."
respectively. The unary plus operator on a frozen string literal returns
an unfrozen copy, which is more idiomatic and performant than String.new.
These mutable strings are necessary because we append to them with <<.

* Fix Lint/AssignmentInCondition and Lint/MixedRegexpCaptureTypes

Lint failures fixed:
- Lint/AssignmentInCondition: Wrapped 'if var = expr' in parentheses to
  clarify that the assignment is intentional, not a typo for '=='.
- Lint/MixedRegexpCaptureTypes: Changed numbered capture groups (\r?\n)
  to non-capturing groups (?:\r?\n) in regex patterns. Mixing named and
  numbered captures causes confusion because numbered captures are
  renumbered when named captures are present, making the regex behavior
  unpredictable.

* Fix Style/RedundantSort in outdated_ruby_version.rb

Lint failure: Style/RedundantSort - Use max_by instead of sort_by...last.

Changed 'sort_by { |v| Gem::Version.new(v) }.last' to
'max_by { |v| Gem::Version.new(v) }'. Using max_by is more efficient as it
finds the maximum in O(n) time without sorting the entire array, and clearly
expresses the intent to find the maximum value.

* Fix Lint/DuplicateMethods in rake_runner.rb

Lint failure: Lint/DuplicateMethods - Method RakeTask#status is defined at
both line 11 (via attr_accessor) and line 37 (explicit def).

The explicit status method includes validation logic that ensures the status
is set to an allowed value before returning it. Changed 'attr_accessor :status'
to 'attr_writer :status' so we only generate the setter, keeping the custom
getter with its validation logic.

* Fix lint violations in ruby.rb

Lint failures fixed:
- Performance/UnfreezeString: Changed String.new("") to +"" for creating
  a mutable string to append to.
- Lint/BinaryOperatorWithIdenticalOperands: Fixed 'version != version' which
  always evaluates to false. Changed to 'old_version != version' to correctly
  detect when the default Node.js/Yarn version has changed. This was a bug
  where the version change warning would never be displayed.
- Lint/IneffectiveAccessModifier: Added file-level ignore in .standard.yml.
  The class has public class methods interspersed after a 'private' declaration
  that only affects instance methods. The class methods are intentionally
  public and called from lib/language_pack.rb.

* Fix lint violations in ruby_version.rb and shell_helpers.rb

Lint failures fixed:
- Style/RedundantInterpolation: Changed "\#{engine_version}" to
  engine_version.to_s and "\#{path}" to path.to_s. String interpolation
  on a single variable is redundant when the variable is already a string
  or should be explicitly converted.
- Removed unused RUBY_VERSION_REGEX constant (dead code).

* Fix lint violations in spec files

Lint failures fixed:
- Lint/RescueException (ruby_spec.rb): Added inline disable. The test
  rescues Exception intentionally to provide debugging output for any failure.
- Style/RedundantInterpolation (outdated_ruby_version_spec.rb): Removed
  redundant string interpolation.
- Lint/ShadowedArgument (ruby_version_spec.rb): Renamed block arg to _dir
  since it's immediately shadowed by a local variable assignment.
- Lint/ConstantDefinitionInBlock (shell_spec.rb): Added inline disable.
  Defining test helper classes inside describe blocks is a common RSpec pattern.
- Lint/DuplicateRequire (spec_helper.rb): Removed duplicate require 'hatchet'.
- Lint/MixedRegexpCaptureTypes (spec_helper.rb): Changed numbered capture
  group to non-capturing group.
- Lint/DuplicateMethods (spec_helper.rb): Removed duplicate hatchet_path
  method definition.

* Fix Layout/IndentationConsistency in ruby.rb

Lint failure: Layout/IndentationConsistency - Inconsistent indentation detected.

Fixed extra indentation on the bundle_command assignment line.

* Changelog

* Remove unused include

* Apply linting fixes
Co-authored-by: heroku-linguist[bot] <136119646+heroku-linguist[bot]@users.noreply.github.com>
Track apps with and without `BUNDLED WITH` in the `Gemfile.lock`. This will show how many apps are using the default bundler version.
# @option options [Boolean] :user_env whether or not a user's environment variables will be loaded
def run(command, options = {})
%x{ #{command_options_to_string(command, options)} }
`#{command_options_to_string(command, options)}`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified an issue in your code:

Shell command injection via unsanitized user input in backtick command execution. Attackers can inject arbitrary commands if the command parameter is controlled by untrusted sources.

More details about this

The run method executes a shell command constructed by interpolating command_options_to_string(command, options) directly into a backtick command. If the command parameter comes from unverified user input, an attacker can inject arbitrary shell code.

Exploit scenario:

  1. An attacker provides a malicious command like "echo safe; rm -rf /" to the run() method
  2. The command_options_to_string() function wraps it: /usr/bin/env bash -c echo safe; rm -rf / 2>&1
  3. The backtick operator executes this in a shell, where the semicolon allows chaining commands
  4. The attacker's injected rm -rf / command runs with the privileges of the Ruby process

Even though command.shellescape is used inside command_options_to_string(), the function returns a string that is then directly interpolated into the backtick without proper quoting, bypassing the escaping protection.

To resolve this comment:

✨ Commit Assistant fix suggestion

Suggested change
`#{command_options_to_string(command, options)}`
require 'open3'
def run(command, options = {})
command_array = command_options_to_array(command, options) # We'll define this helper below
stdout_str, status = Open3.capture2e(*command_array)
stdout_str
end
# Helper method to return an array for command and args for Open3
def command_options_to_array(command, options)
options[:env] ||= {}
options[:env] = user_env_hash.merge(options[:env]) if options[:user_env]
env = options[:env].transform_keys(&:to_s)
# Build the bash command part safely
bash_command = command.to_s
# Anything to redirect output (e.g., "2>&1") is best handled in Ruby itself, not shell, to avoid injection.
# We'll ignore options[:out] to avoid passing dangerous redirects; Open3.capture2e already merges stderr into stdout.
[env, '/usr/bin/env', 'bash', '-c', bash_command]
end
View step-by-step instructions
  1. Avoid using backticks with string interpolation to execute shell commands, as this can allow command injection if any part of the command or options is user-controlled.
  2. Replace the line `#{command_options_to_string(command, options)}` with usage of the safer Open3 or system method, which accepts command and arguments as an array.
  3. Refactor command_options_to_string to return an array: for example, split up the command and its arguments so you can pass them to Open3.capture2e or system.
  4. In the run method, replace the dangerous call with: Open3.capture2e(*command_array) (after requiring open3 at the top of the file: require 'open3'), where command_array is the securely built command and its arguments.
  5. Make sure to never pass user-controlled input directly to the shell unless properly validated or escaped.

Alternatively, if you must use backticks,
validate and strictly sanitize all parts of the command string to only allow safe, expected values. This is less robust than avoiding the shell entirely.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by dangerous-subshell.

You can view more details about this finding in the Semgrep AppSec Platform.

Frzk and others added 6 commits February 2, 2026 18:04
If your `Gemfile.lock` doesn’t include the `BUNDLED WITH` key, Heroku installs a [default bundler version](https://devcenter.heroku.com/articles/ruby-support-reference#default-bundler-version):

- Apps using [Classic Buildpacks](https://devcenter.heroku.com/articles/buildpacks#classic-buildpacks) was `2.3.25` now `2.5.23`
- Apps using [Cloud Native Buildpacks](https://devcenter.heroku.com/articles/buildpacks#heroku-cloud-native-buildpacks) stays `2.5.23`

>note
>Ruby's [standard version of bundler](https://stdgems.org/bundler/) takes precedence if it's greater than Heroku's installed version. When there is no `BUNDLED WITH` in the `Gemfile.lock`, then `bundle install` uses the highest version of Bundler available.

It is strongly recommended that you have both a `RUBY VERSION` and `BUNDLED WITH` version listed in your `Gemfile.lock`. If you do not have those values, you can generate them and commit them to git:

```
$ bundle update --ruby
$ git add Gemfile.lock
$ git commit -m "Update Gemfile.lock"
```

Applications without these values specified in the `Gemfile.lock` may break unexpectedly when the defaults change.

This is an alternative to downgrading the CNB version heroku#1706 (comment).
…eroku#1709)

Bumps the ruby-dependencies group with 1 update: [standard](https://github.com/standardrb/standard).


Updates `standard` from 1.52.0 to 1.53.0
- [Release notes](https://github.com/standardrb/standard/releases)
- [Changelog](https://github.com/standardrb/standard/blob/main/CHANGELOG.md)
- [Commits](standardrb/standard@v1.52.0...v1.53.0)

---
updated-dependencies:
- dependency-name: standard
  dependency-version: 1.53.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: ruby-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: heroku-linguist[bot] <136119646+heroku-linguist[bot]@users.noreply.github.com>
Co-authored-by: heroku-linguist[bot] <136119646+heroku-linguist[bot]@users.noreply.github.com>
@Frzk Frzk changed the title Sync with upstream v345 Sync with upstream v347 Feb 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants