diff --git a/.gitlab/merge_request_templates/new_feature.md b/.gitlab/merge_request_templates/new_feature.md
index 74abae94c94dc0768bb5c51fe51ad253fce113fe..491b7f98ded7e0da03d18c95978eafcb7d86619f 100644
--- a/.gitlab/merge_request_templates/new_feature.md
+++ b/.gitlab/merge_request_templates/new_feature.md
@@ -8,8 +8,8 @@ Closes #999
 ## Checklist
 
 * General:
-    * [ ] use [rules](https://docs.gitlab.com/ee/ci/yaml/#rules) instead of [only/except](https://docs.gitlab.com/ee/ci/yaml/#onlyexcept-advanced)
-    * [ ] optimized [cache](https://docs.gitlab.com/ee/ci/caching/) configuration (wherever applicable)
+    * [ ] use [rules](https://docs.gitlab.com/ci/yaml/#rules) instead of [only/except](https://docs.gitlab.com/ci/yaml/#onlyexcept-advanced)
+    * [ ] optimized [cache](https://docs.gitlab.com/ci/caching/) configuration (wherever applicable)
 * Publicly usable:
     * [ ] untagged runners
     * [ ] no proxy configuration but support `http_proxy`/`https_proxy`/`no_proxy`
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9165b6e4cf6300cba9b40f838928d50fc4c8872b..9991db63fd61546a4997db1bae214fe18d7a1b86 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,18 @@
-# [4.2.0](https://git.code.tecnalia.dev/smartdatalab/public/ci-cd-components/node/compare/4.1.1...4.2.0) (2025-01-29)
+## [4.2.2](https://gitlab.com/to-be-continuous/node/compare/4.2.1...4.2.2) (2025-04-16)
+
+
+### Bug Fixes
+
+* **pnpm:** double dash not supported by pnpm ([776ddb5](https://gitlab.com/to-be-continuous/node/commit/776ddb5168174108f2d1c01a0bfaddd8e9e293d9))
+
+## [4.2.1](https://gitlab.com/to-be-continuous/node/compare/4.2.0...4.2.1) (2025-01-31)
+
+
+### Bug Fixes
+
+* **sbom:** only generate SBOMs on prod branches, integ branches and release tags ([b61bdcd](https://gitlab.com/to-be-continuous/node/commit/b61bdcdc294567e78771e83fc2fceb122a3c16fe))
+
+# [4.2.0](https://gitlab.com/to-be-continuous/node/compare/4.1.1...4.2.0) (2025-01-27)
 
 
 ### Features
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index edd3476236b8340a401245b3b2b43dde10bd9baf..a71fad78a80c442bbcf3ebc9cc615840523f4e05 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -61,7 +61,7 @@ To contribute:
 
 1. Create an issue describing the bug or enhancement you want to propose (select the right issue template).
 2. Make sure the issue has been reviewed and agreed.
-3. Create a Merge Request, from your **own** fork (see [forking workflow](https://docs.gitlab.com/ee/user/project/repository/forking_workflow.html) documentation).
+3. Create a Merge Request, from your **own** fork (see [forking workflow](https://docs.gitlab.com/user/project/repository/forking_workflow/) documentation).
    Don't hesitate to mark your MR as `Draft` as long as you think it's not ready to be reviewed.
 
 ### Git Commit Conventions
diff --git a/README.md b/README.md
index 6e624b39177faf85ffdb584aec71136572308613..f47259c0f95f7bc0ad9428bba4f59409b268f095 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,8 @@ More precisely, it can be used by all projects based on [npm](https://www.npmjs.
 
 ## Usage
 
-This template can be used both as a [CI/CD component](https://docs.gitlab.com/ee/ci/components/#use-a-component)
-or using the legacy [`include:project`](https://docs.gitlab.com/ee/ci/yaml/index.html#includeproject) syntax.
+This template can be used both as a [CI/CD component](https://docs.gitlab.com/ci/components/#use-a-component)
+or using the legacy [`include:project`](https://docs.gitlab.com/ci/yaml/#includeproject) syntax.
 
 ### Use as a CI/CD component
 
@@ -16,11 +16,11 @@ Add the following to your `.gitlab-ci.yml`:
 ```yaml
 include:
   # 1: include the component
-  - component: $CI_SERVER_FQDN/to-be-continuous/node/gitlab-ci-node@4.2.0
+  - component: $CI_SERVER_FQDN/to-be-continuous/node/gitlab-ci-node@4.2.2
     # 2: set/override component inputs
     inputs:
       image: "registry.hub.docker.com/library/node:20" # ⚠ this is only an example
-      lint-enabled: "true"
+      lint-enabled: true
 ```
 
 ### Use as a CI/CD template (legacy)
@@ -31,7 +31,7 @@ Add the following to your `.gitlab-ci.yml`:
 include:
   # 1: include the template
   - project: "to-be-continuous/node"
-    ref: "4.2.0"
+    ref: "4.2.2"
     file: "/templates/gitlab-ci-node.yml"
 
 variables:
@@ -46,7 +46,7 @@ The Node.js template uses some global configuration used throughout all jobs.
 
 | Input / Variable                                             | Description                                                                                                                                                                                                                                                | Default value                                     |
 | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
-| `image` / `NODE_IMAGE`                                       | The Docker image used to run Node.js <br/>:warning: **set the version required by your project**                                                                                                                                                           | `registry.hub.docker.com/library/node:lts-alpine` |
+| `image` / `NODE_IMAGE`                                       | The Docker image used to run Node.js <br/>:warning: **set the version required by your project**                                                                                                                                                           | `registry.hub.docker.com/library/node:lts-alpine` <br/>[![Trivy Badge](https://to-be-continuous.gitlab.io/doc/secu/trivy-badge-NODE_IMAGE.svg)](https://to-be-continuous.gitlab.io/doc/secu/trivy-NODE_IMAGE) |
 | `manager` / `NODE_MANAGER`                                   | The package manager used by your project (one of `npm`, `yarn` or `pnpm`)<br/>**If undefined, automatic detection**                                                                                                                                        | _none_ (auto)                                     |
 | `config-registry` / `NODE_CONFIG_REGISTRY`                   | Main npm [registry](https://docs.npmjs.com/cli/v8/using-npm/registry) to use                                                                                                                                                                               | _none_                                            |
 | `config-scoped-registries` / `NODE_CONFIG_SCOPED_REGISTRIES` | Space separated list of npm [scoped registries](https://docs.npmjs.com/cli/v8/using-npm/scope#associating-a-scope-with-a-registry) (formatted as `@somescope:https://some.npm.registry/some/repo @anotherscope:https://another.npm.registry/another/repo`) | _none_                                            |
@@ -64,7 +64,7 @@ Examples:
 - `npm install @angular/core` installs `@angular/core` package from https://www.npmjs.com/ if no npm registry associated to scope `@angular` is declared,
 - `npm install @acme-corp/bar` installs `@acme-corp/bar` package from https://registry.acme.corp/npm if this registry url is associated to scope `@acme-corp`.
 
-First of all, be aware that the Node.js template automatically configures the [GitLab's project-level npm packages registry](https://docs.gitlab.com/ee/user/packages/npm_registry/) associated to a scope corresponding to the root of the project (ex: project `https://gitlab.example.com/my-org/engineering-group/team-amazing/analytics` will have GitLab's project-level npm packages registry scope `@my-org`).
+First of all, be aware that the Node.js template automatically configures the [GitLab's project-level npm packages registry](https://docs.gitlab.com/user/packages/npm_registry/) associated to a scope corresponding to the root of the project (ex: project `https://gitlab.example.com/my-org/engineering-group/team-amazing/analytics` will have GitLab's project-level npm packages registry scope `@my-org`).
 Therefore, GitLab's project-level npm packages registry can freely be used both to install packages (with the right scope) or even to [publish](#node-publish-job) your own packages.
 
 You may configure additional [scoped registries](https://docs.npmjs.com/cli/v8/using-npm/scope#associating-a-scope-with-a-registry) with the `$NODE_CONFIG_SCOPED_REGISTRIES` variable.
@@ -105,7 +105,7 @@ In addition to a textual report in the console, this job produces the following
 
 | Report                                                | Format                                                   | Usage                                                                                                       |
 |-------------------------------------------------------|----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
-| `$NODE_PROJECT_DIR/reports/node-lint.gitlab.json` | [GitLab](https://docs.gitlab.com/ee/ci/testing/code_quality.html#eslint) | [GitLab integration](https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportscodequality) |
+| `$NODE_PROJECT_DIR/reports/node-lint.gitlab.json` | [GitLab](https://docs.gitlab.com/ci/testing/code_quality/#eslint) | [GitLab integration](https://docs.gitlab.com/ci/yaml/artifacts_reports/#artifactsreportscodequality) |
 | `$NODE_PROJECT_DIR/reports/node-lint.xslint.json` | JSON ESLint                                               | [SonarQube integration](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/importing-external-issues/external-analyzer-reports/)                        |
 
 
@@ -134,7 +134,7 @@ This job is bound to the `build` stage, and uses the following variables:
 #### Unit Tests and Code Coverage reports
 
 This chapter details the required configuration (depending on the unit testing framework you're using) in
-order to integrate your [unit tests reports](https://docs.gitlab.com/ee/ci/testing/unit_test_reports.html) and [code coverage reports](https://docs.gitlab.com/ee/ci/testing/test_coverage_visualization.html) to GitLab.
+order to integrate your [unit tests reports](https://docs.gitlab.com/ci/testing/unit_test_reports/) and [code coverage reports](https://docs.gitlab.com/ci/testing/code_coverage/) to GitLab.
 
 Additionally, if also using SonarQube, you'll have to enable some extra reporters.
 
@@ -144,9 +144,9 @@ Here is the required configuration if you're using [Jest](https://jestjs.io/) as
 
 | Reporter                                                                                     | Needs `npm install` | Expected report file             | Usage                                                                                                                                                              |
 | -------------------------------------------------------------------------------------------- | ------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| [jest-junit](https://github.com/jest-community/jest-junit)                                   | Yes                 | `reports/node-test.xunit.xml`    | [GitLab unit tests integration](https://docs.gitlab.com/ee/ci/testing/unit_test_reports.html) _(JUnit format)_                                                     |
-| istanbul [text](https://istanbul.js.org/docs/advanced/alternative-reporters/#text)           | No                  | N/A _(stdout)_                   | [GitLab MR test coverage results](https://docs.gitlab.com/ee/ci/pipelines/settings.html#merge-request-test-coverage-results) _(GitLab grabs coverage from stdout)_ |
-| istanbul [cobertura](https://istanbul.js.org/docs/advanced/alternative-reporters/#cobertura) | No                  | `reports/cobertura-coverage.xml` | [GitLab code coverage integration](https://docs.gitlab.com/ee/ci/testing/test_coverage_visualization.html) _(Cobertura format)_                                    |
+| [jest-junit](https://github.com/jest-community/jest-junit)                                   | Yes                 | `reports/node-test.xunit.xml`    | [GitLab unit tests integration](https://docs.gitlab.com/ci/testing/unit_test_reports/) _(JUnit format)_                                                     |
+| istanbul [text](https://istanbul.js.org/docs/advanced/alternative-reporters/#text)           | No                  | N/A _(stdout)_                   | [GitLab MR test coverage results](https://docs.gitlab.com/ci/pipelines/settings/#merge-request-test-coverage-results) _(GitLab grabs coverage from stdout)_ |
+| istanbul [cobertura](https://istanbul.js.org/docs/advanced/alternative-reporters/#cobertura) | No                  | `reports/cobertura-coverage.xml` | [GitLab code coverage integration](https://docs.gitlab.com/ci/testing/code_coverage/) _(Cobertura format)_                                    |
 | [jest-sonar](https://github.com/sh33dafi/jest-sonar)                                         | Yes                 | `reports/node-test.sonar.xml`    | [SonarQube unit tests integration](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/test-coverage/generic-test-data/#generic-test-coverage) _(generic SonarQube format)_                                          |
 | istanbul [lcovonly](https://istanbul.js.org/docs/advanced/alternative-reporters/#lcovonly)   | No                  | `reports/lcov.info`              | [SonarQube code coverage integration](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/test-coverage/javascript-typescript-test-coverage/) _(JS/TS LCOV format)_         |
 
@@ -191,9 +191,9 @@ Here is the required configuration if you're using [Mocha](https://mochajs.org/)
 
 | Reporter                                                                                     | Needs `npm install`    | Expected report file             | Usage                                                                                                                                                              |
 | -------------------------------------------------------------------------------------------- | ---------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| [mocha-junit-reporter](https://github.com/michaelleeallen/mocha-junit-reporter)              | Yes                    | `reports/node-test.xunit.xml`    | [GitLab unit tests integration](https://docs.gitlab.com/ee/ci/testing/unit_test_reports.html) _(JUnit format)_                                                     |
-| istanbul [text](https://istanbul.js.org/docs/advanced/alternative-reporters/#text)           | Yes (in `nyc` package) | N/A _(stdout)_                   | [GitLab MR test coverage results](https://docs.gitlab.com/ee/ci/pipelines/settings.html#merge-request-test-coverage-results) _(GitLab grabs coverage from stdout)_ |
-| istanbul [cobertura](https://istanbul.js.org/docs/advanced/alternative-reporters/#cobertura) | Yes (in `nyc` package) | `reports/cobertura-coverage.xml` | [GitLab code coverage integration](https://docs.gitlab.com/ee/ci/testing/test_coverage_visualization.html) _(Cobertura format)_                                    |
+| [mocha-junit-reporter](https://github.com/michaelleeallen/mocha-junit-reporter)              | Yes                    | `reports/node-test.xunit.xml`    | [GitLab unit tests integration](https://docs.gitlab.com/ci/testing/unit_test_reports/) _(JUnit format)_                                                     |
+| istanbul [text](https://istanbul.js.org/docs/advanced/alternative-reporters/#text)           | Yes (in `nyc` package) | N/A _(stdout)_                   | [GitLab MR test coverage results](https://docs.gitlab.com/ci/pipelines/settings/#merge-request-test-coverage-results) _(GitLab grabs coverage from stdout)_ |
+| istanbul [cobertura](https://istanbul.js.org/docs/advanced/alternative-reporters/#cobertura) | Yes (in `nyc` package) | `reports/cobertura-coverage.xml` | [GitLab code coverage integration](https://docs.gitlab.com/ci/testing/code_coverage/) _(Cobertura format)_                                    |
 | [mocha-sonarqube-reporter](https://github.com/mmouterde/mocha-sonarqube-reporter)            | Yes                    | `reports/node-test.sonar.xml`    | [SonarQube unit tests integration](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/test-coverage/generic-test-data/#generic-test-coverage) _(generic SonarQube format)_                                          |
 | istanbul [lcovonly](https://istanbul.js.org/docs/advanced/alternative-reporters/#lcovonly)   | Yes (in `nyc` package) | `reports/lcov.info`              | [SonarQube code coverage integration](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/test-coverage/javascript-typescript-test-coverage/) _(JS/TS LCOV format)_         |
 
@@ -387,7 +387,7 @@ It is bound to the `test` stage, and uses the following variables:
 | Input / Variable                                                         | Description                                                                                                                                                                                                              | Default value                                                |
 | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |
 | `semgrep-disabled` / `NODE_SEMGREP_DISABLED`                             | Set to `true` to disable this job                                                                                                                                                                                        | _none_                                                       |
-| `semgrep-image` / `NODE_SEMGREP_IMAGE`                                   | The Docker image used to run [Semgrep](https://semgrep.dev/docs/)                                                                                                                                                        | `registry.hub.docker.com/semgrep/semgrep:latest`             |
+| `semgrep-image` / `NODE_SEMGREP_IMAGE`                                   | The Docker image used to run [Semgrep](https://semgrep.dev/docs/)                                                                                                                                                        | `registry.hub.docker.com/semgrep/semgrep:latest`             <br/>[![Trivy Badge](https://to-be-continuous.gitlab.io/doc/secu/trivy-badge-NODE_SEMGREP_IMAGE.svg)](https://to-be-continuous.gitlab.io/doc/secu/trivy-NODE_SEMGREP_IMAGE) |
 | `semgrep-args` / `NODE_SEMGREP_ARGS`                                     | Semgrep [scan options](https://semgrep.dev/docs/cli-reference#semgrep-scan-command-options)                                                                                                                              | `--metrics off --disable-version-check --no-suppress-errors` |
 | `semgrep-rules` / `NODE_SEMGREP_RULES`                                   | Space-separated list of [Semgrep rules](https://semgrep.dev/docs/running-rules).<br/>Can be both local YAML files or remote rules from the [Semgrep Registry](https://semgrep.dev/explore) (denoted by the `p/` prefix). | `p/javascript p/eslint p/gitlab-eslint`                      |
 | `semgrep-registry-base-url` / `NODE_SEMGREP_REGISTRY_BASE_URL`           | The Semgrep Registry base URL that is used to download the rules. No trailing slash.                                                                                                                                     | `https://semgrep.dev/c`                                      |
@@ -405,7 +405,7 @@ In addition to a textual report in the console, this job produces the following
 
 | Report                                               | Format                                                                                       | Usage                                                                                                                                                                   |
 | ---------------------------------------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `$NODE_PROJECT_DIR/reports/node-semgrep.gitlab.json` | [GitLab's SAST format](https://semgrep.dev/docs/cli-reference#semgrep-scan-command-options)  | [GitLab integration](https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportssast)                                                                    |
+| `$NODE_PROJECT_DIR/reports/node-semgrep.gitlab.json` | [GitLab's SAST format](https://semgrep.dev/docs/cli-reference#semgrep-scan-command-options)  | [GitLab integration](https://docs.gitlab.com/ci/yaml/artifacts_reports/#artifactsreportssast)                                                                    |
 | `$NODE_PROJECT_DIR/reports/node-semgrep.native.json` | [Semgrep's JSON format](https://semgrep.dev/docs/cli-reference#semgrep-scan-command-options) | [DefectDojo integration](https://docs.defectdojo.com/en/connecting_your_tools/parsers/file/semgrep/)<br/>_This report is generated only if DefectDojo template is detected_ |
 
 ### `node-sbom` job
@@ -417,6 +417,7 @@ It is bound to the `test` stage, and uses the following variables:
 | Input / Variable                       | Description                                                 | Default value        |
 | -------------------------------------- | ----------------------------------------------------------- | -------------------- |
 | `sbom-disabled` / `NODE_SBOM_DISABLED` | Set to `true` to disable this job                           | _none_               |
+| `TBC_SBOM_MODE`                        | Controls when SBOM reports are generated (`onrelease`: only on `$INTEG_REF`, `$PROD_REF` and `$RELEASE_REF` pipelines; `always`: any pipeline).<br/>:warning: `sbom-disabled` / `NODE_SBOM_DISABLED` takes precedence | `onrelease` |
 | `sbom-version` / `NODE_SBOM_VERSION`   | The version of @cyclonedx/cyclonedx-npm used to emit SBOM   | _none_ (uses latest) |
 | `sbom-opts` / `NODE_SBOM_OPTS`         | Options for @cyclonedx/cyclonedx-npm used for SBOM analysis | `--omit dev`         |
 | `node-sbom-job-tags` / `NODE_SBOM_JOB_TAGS` | Tags to be used for selecting runners for the job | [] |
@@ -473,7 +474,7 @@ Then simply declare the registry authentication token with :lock: `NODE_PUBLISH_
 
 :information_source: it is not mandatory to declare the registry if you wish to use the GitLab
 project-level npm packages registry (it is declared by default by the template, with the required credentials). All you have to do to is to make sure your npm package name
-[uses the right scope](https://docs.gitlab.com/ee/user/packages/npm_registry/#naming-convention).
+[uses the right scope](https://docs.gitlab.com/user/packages/npm_registry/#naming-convention).
 For example, if your project is `https://gitlab.example.com/my-org/engineering-group/team-amazing/analytics`, the root namespace is `my-org`, and your package name must have the `@my-org` scope (probable package fullname: `@my-org/analytics`).
 
 #### Exclude resources from package
@@ -505,7 +506,7 @@ In order to be able to communicate with the Vault server, the variant requires t
 | :lock: `VAULT_ROLE_ID`              | The [AppRole](https://www.vaultproject.io/docs/auth/approle) RoleID                                                             | _none_                                                                     |
 | :lock: `VAULT_SECRET_ID`            | The [AppRole](https://www.vaultproject.io/docs/auth/approle) SecretID                                                           | _none_                                                                     |
 
-By default, the variant will authentifacte using a [JWT ID token](https://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html). To use [AppRole](https://www.vaultproject.io/docs/auth/approle) instead the `VAULT_ROLE_ID` and `VAULT_SECRET_ID` should be defined as secret project variables.
+By default, the variant will authentifacte using a [JWT ID token](https://docs.gitlab.com/ci/secrets/id_token_authentication/). To use [AppRole](https://www.vaultproject.io/docs/auth/approle) instead the `VAULT_ROLE_ID` and `VAULT_SECRET_ID` should be defined as secret project variables.
 
 #### Usage
 
@@ -527,9 +528,9 @@ With:
 ```yaml
 include:
   # main template
-  - component: $CI_SERVER_FQDN/to-be-continuous/node/gitlab-ci-node@4.2.0
+  - component: $CI_SERVER_FQDN/to-be-continuous/node/gitlab-ci-node@4.2.2
   # Vault variant
-  - component: $CI_SERVER_FQDN/to-be-continuous/node/gitlab-ci-node-vault@4.2.0
+  - component: $CI_SERVER_FQDN/to-be-continuous/node/gitlab-ci-node-vault@4.2.2
     inputs:
       # audience claim for JWT
       vault-oidc-aud: "https://vault.acme.host"
diff --git a/kicker.json b/kicker.json
index 0d2b302865f5abc8dfbe0f07a5e3534fe24a37db..ae3d1543095a120e7463246d53608cebddcfbdb2 100644
--- a/kicker.json
+++ b/kicker.json
@@ -189,6 +189,14 @@
       "description": "This job generates a file listing all dependencies using [@cyclonedx/cyclonedx-npm](https://www.npmjs.com/package/@cyclonedx/cyclonedx-npm)",
       "disable_with": "NODE_SBOM_DISABLED",
       "variables": [
+        {
+          "name": "TBC_SBOM_MODE",
+          "type": "enum",
+          "values": ["onrelease", "always"],
+          "description": "Controls when SBOM reports are generated (`onrelease`: only on `$INTEG_REF`, `$PROD_REF` and `$RELEASE_REF` pipelines; `always`: any pipeline)",
+          "advanced": true,
+          "default": "onrelease"
+        },
         {
           "name": "NODE_SBOM_VERSION",
           "description": "Version of the @cyclonedx/cyclonedx-npm used for SBOM analysis",
diff --git a/templates/gitlab-ci-node-vault.yml b/templates/gitlab-ci-node-vault.yml
index 5744eeef6ffa48a6f41def651e6703b2c72edfac..7d013fe6f2322b8e4ae3834831a6ccbb71bdc3ed 100644
--- a/templates/gitlab-ci-node-vault.yml
+++ b/templates/gitlab-ci-node-vault.yml
@@ -22,7 +22,7 @@ variables:
 .node-base:
   services:
     - name: "$TBC_TRACKING_IMAGE"
-      command: ["--service", "node", "4.2.0"]
+      command: ["--service", "node", "4.2.2"]
     - name: "$TBC_VAULT_IMAGE"
       alias: "vault-secrets-provider"
   variables:
diff --git a/templates/gitlab-ci-node.yml b/templates/gitlab-ci-node.yml
index 707f27cc076d84e42968185f6778dc66e27d7a01..a646a747d81f2cae13d546d51c04aba1e84759e9 100644
--- a/templates/gitlab-ci-node.yml
+++ b/templates/gitlab-ci-node.yml
@@ -193,7 +193,18 @@ workflow:
     # else (Ready MR): auto & failing
     - when: on_success
 
+# software delivery job prototype: run on production and integration branches + release pipelines
+.delivery-policy:
+  rules:
+    # on tag with release pattern
+    - if: '$CI_COMMIT_TAG =~ $RELEASE_REF'
+    # on production or integration branch(es)
+    - if: '$CI_COMMIT_REF_NAME =~ $PROD_REF || $CI_COMMIT_REF_NAME =~ $INTEG_REF'
+
 variables:
+  # Global TBC SBOM Mode (onrelease -> only generate SBOMs for releases, always -> generate SBOMs for all refs)
+  TBC_SBOM_MODE: "onrelease"
+
   # Default docker image for Node
   NODE_IMAGE: $[[ inputs.image ]]
   # Default Node project root directory
@@ -633,7 +644,7 @@ stages:
   image: $NODE_IMAGE
   services:
     - name: "$TBC_TRACKING_IMAGE"
-      command: ["--service", "node", "4.2.0"]
+      command: ["--service", "node", "4.2.2"]
   variables:
     # Yarn cache (better than --cache-folder option, deprecated)
     YARN_CACHE_FOLDER: "$CI_PROJECT_DIR/$NODE_PROJECT_DIR/.yarn"
@@ -715,7 +726,12 @@ node-lint:
         # generate eslint report for SonarQube
         # shellcheck disable=SC2086
         log_info "SonarQube detected: producing ESLint JSON report..."
-        $NODE_MANAGER $NODE_LINT_ARGS -- --format=json --output-file=reports/node-lint.xslint.json || true
+        if [ "$NODE_MANAGER" = "pnpm" ]
+        then
+          $NODE_MANAGER $NODE_LINT_ARGS --format=json --output-file=reports/node-lint.xslint.json || true
+        else
+          $NODE_MANAGER $NODE_LINT_ARGS -- --format=json --output-file=reports/node-lint.xslint.json || true
+        fi
       fi
     # maybe add eslint-formatter-gitlab
     - |
@@ -726,7 +742,13 @@ node-lint:
       fi
     # run ESLint with console output and GitLab report
     # shellcheck disable=SC2086
-    - ESLINT_CODE_QUALITY_REPORT=reports/node-lint.gitlab.json $NODE_MANAGER $NODE_LINT_ARGS -- --format=gitlab
+    - |
+      if [ "$NODE_MANAGER" = "pnpm" ]
+      then
+        ESLINT_CODE_QUALITY_REPORT=reports/node-lint.gitlab.json $NODE_MANAGER $NODE_LINT_ARGS --format=gitlab
+      else
+        ESLINT_CODE_QUALITY_REPORT=reports/node-lint.gitlab.json $NODE_MANAGER $NODE_LINT_ARGS -- --format=gitlab
+      fi
   artifacts:
     when: always # store artifact even if test Failed
     name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
@@ -869,7 +891,13 @@ node-sbom:
     # exclude if disabled
     - if: '$NODE_SBOM_DISABLED == "true"'
       when: never
-    - !reference [.test-policy, rules]
+    # 'always' mode: run
+    - if: '$TBC_SBOM_MODE == "always"'
+    # exclude unsupported modes
+    - if: '$TBC_SBOM_MODE != "onrelease"'
+      when: never
+    # 'onrelease' mode: use common software delivery rules
+    - !reference [.delivery-policy, rules]
   tags: $[[ inputs.node-sbom-job-tags ]]
 
 node-publish: