ci: add pinned security scanning workflows - #6
Conversation
Add SARIF-publishing security scans for the release branch, following the pattern already used across the intel org (harden-runner + per-tool workflow files + OpenSSF Scorecard): - trivy vulnerabilities, secrets, misconfig, licenses (filesystem) - bandit Python security findings - checkov Kubernetes / Actions / secrets IaC checks - shellcheck shell script correctness and quoting - ansible-lint Ansible role and playbook hygiene - scorecard OpenSSF supply-chain posture rating Every action is pinned to a full commit SHA and every tool to an exact version, so a given commit always scans the same way and version bumps are explicit, reviewable diffs. ShellCheck is fetched from its upstream release and checksum-verified rather than installed via apt, whose version drifts with the runner image. Each scan was run locally against the tree before landing, and two were retuned as a result: - checkov drops the `helm` framework. Checkov renders charts into a random temp dir and reports paths relative to it, so all 50 (ai-solutions) / 40 (inference) findings pointed at files that do not exist in the repo -- unanchorable in the Security tab and re-reported as new every run. They were also entirely in vendored upstream charts. Trivy's misconfig scanner covers chart misconfiguration with correct paths. - ansible-lint skips style-only rule families, which accounted for 1158 of 1163 findings in ai-solutions and 496 of 502 in inference. name[casing] alone fired 350 times against the deliberate `role | action | desc` naming convention. Jobs are soft-fail for the initial rollout: findings surface under Security -> Code scanning instead of blocking the branch, so the existing backlog can be triaged before any check is made required. CodeQL is intentionally not added here - it already runs via code scanning default setup on both repositories.
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
610b526 to
b2a136f
Compare
Triage of every finding the scans produced, separating defects that cannot be
true from ones worth a maintainer's time. Every remaining finding was checked by
hand against the source and is real.
ShellCheck (33 -> 7)
Discovery now reads shebangs as well as *.sh suffixes: model_manager/model-
manager is the largest script in the tree at 1429 lines and a *.sh glob never
saw it.
Entry points are linted with -x so the helper scripts they source are
followed, plus a --source-path list -- the source statements are built from
"$MM_LIB/..." which ShellCheck cannot resolve unaided, and without it 7
globals that model_manager/lib/ does set and model-manager does read looked
unused.
The helper scripts under model_manager/lib/ are linted on their own.
ShellCheck sees one file at a time, so a global a helper sets for its caller
reads as unused: 26 of the 31 SC2034 hits were that. Disabling SC2034 for
those files was too blunt -- it also hid four globals that genuinely are
written and never read anywhere. Each hit is now checked individually and
suppressed only when another file demonstrably reads that variable, limited to
SHOUTY_CASE names of 3+ characters since a short lowercase name such as a
loop's `i` collides textually with unrelated files.
Kept, all verified by grepping the whole tree:
- config.sh:266 RUNTIME_DEVICE, parsed from the runtime JSON beside five
siblings that are all consumed, then never read -- cpu/gpu selection is
read and dropped.
- nri.sh:82,83 NRI_ADVANCED_MODE and NRI_HIDE_HT, both named in the
function's own "Sets:" header comment as outputs, neither consumed. The
Python side reads the MM_NRI_HIDE_HT environment variable, not this.
- nri.sh:241 NRI_TARGET_NODE, set beside NRI_POLICY_NAME which is used.
- helpers.sh:244 `local i` in a `for i in $(seq 1 24)` retry loop whose body
never uses it.
- helpers.sh:88, routing.sh:288 SC2155.
No file under model_manager/ was modified.
Bandit (0 -> 0)
Skips B404 (importing subprocess is not a defect) and B603, which fires on
the recommended argv-list form and cannot judge whether input is trusted.
shell=True and friends (B602/B604/B605/B609) stay enabled. Severity is
deliberately not filtered, since real tests such as B105 report LOW.
Trivy (17 -> 1)
`limit-severities-for-sarif` defaults to false, and unset it makes the action
drop --severity for the SARIF run alone -- the table reported 0 findings
while the uploaded SARIF carried all 17 at LOW/MEDIUM, every one below the
declared CRITICAL,HIGH threshold. Now split in two: CVEs, secrets and
licences at CRITICAL,HIGH with the limit applied; misconfiguration at every
severity, because Trivy rates nearly every Kubernetes check LOW or MEDIUM and
the same threshold would silence that scanner outright. Noise is controlled
by scope instead -- vendored charts/litellm-helm accounted for 16 of the 17
and is now skipped, matching the Checkov workflow.
Kept: KSV-0106 in roles/llm_services/files/runtimes/vllm-runtime.yaml, the
one finding in code this repo owns.
ansible-lint, Checkov
No false positives; the 4 ansible-lint findings stand.
Verified locally against the pinned tool versions, under `bash -e` as Actions
runs it. actionlint clean.
mramotowski
left a comment
There was a problem hiding this comment.
Overall it looks good, some minor comments.
| # Style-only rule families are skipped: on these repos they produced 1158 | ||
| # (ai-solutions) / 501 (inference) findings, of which ~5 were substantive. | ||
| # name[casing] alone fought the repo's deliberate `role | action | desc` | ||
| # convention 350 times. What is left is correctness and command-shell risk. |
There was a problem hiding this comment.
Is this comment needed? Why do we care about ai-solutions in enterprise-inference GH workflow?
There was a problem hiding this comment.
Agreed, dropped. Justifying this repo's skip list by citing another repo's numbers was noise. The comment now carries only this repo's figures, re-measured: 499 findings unfiltered, of which 4 are substantive, with name[casing] accounting for 350 of them. Fixed in 810ec5e.
There was a problem hiding this comment.
Correcting the numbers I gave above: the baseline is 501 unfiltered / 6 substantive, not 499 / 4. name[casing] is unchanged at 350.
I had measured locally, where ansible-lint silently skips schema validation because it cannot fetch the JSON schemas, so two schema[meta] findings never appeared. They do in CI:
2 schema profile:basic tags:core
1 risky-shell-pipe profile:basic tags:command-shell
3 no-changed-when profile:basic tags:command-shell,idempotency
Failed: 6 failure(s), 0 warning(s)
roles/langfuse/meta/main.yaml and roles/litellm/meta/main.yaml are the only role metas declaring galaxy_info, and the schema requires author whenever it is present. Both are genuine, and I have left them unfixed — consistent with every other real finding this PR reports rather than resolves.
ai-solutions is unaffected: schema is an unskippable rule, so its filtered CI run would have surfaced any schema finding, and it reported none. Comment corrected in 925010b.
| pull_request: | ||
| types: [opened, synchronize, reopened, ready_for_review] | ||
| schedule: | ||
| # Weekly re-scan so new findings surface without a code change. |
There was a problem hiding this comment.
It is true but it won't work on branches. Schedule is working only on default branch (main) so we will need to make this PR to main branch also to enable this trigger.
There was a problem hiding this comment.
Correct, and thanks — that would have been a silent trap. I have put the caveat in the file itself rather than leaving it implicit, on all four scheduled workflows (trivy, bandit, checkov, scorecard):
schedule:
# Weekly re-scan so new findings surface without a code change.
# NOTE: GitHub only fires `schedule` on the repository's default
# branch, so this trigger stays dormant until these workflows are
# also merged to `main`. The push and pull_request triggers above
# work on every listed branch regardless.So the weekly re-scan starts working once this lands on main; push and pull_request are unaffected on release-1.0 in the meantime.
| bandit -r . -x ./venv,./.venv,./tests -s B101,B404,B601,B603 \ | ||
| -f sarif -o bandit-results.sarif --exit-zero | ||
| bandit -r . -x ./venv,./.venv,./tests -s B101,B404,B601,B603 -f txt --exit-zero |
There was a problem hiding this comment.
We are running same cmd with different outputs twice. It doubles execution time. I know that it is short but still maybe we could do something like:
bandit -r . -x ./venv,./.venv,./tests -s B101,B404,B601,B603 \
-f sarif -o bandit-results.sarif --exit-zero && \
cat bandit-results.sarif | python3 -m json.tool | grep -E '"(ruleId|message|level)"' || true
There was a problem hiding this comment.
Good catch — the second invocation existed only to print something readable and re-scanned the whole tree to do it. Now a single scan, with the summary rendered from the SARIF that scan already wrote. I went with jq over grep on json.tool output so the rule, level and file:line stay associated per finding rather than becoming three separate streams of matches:
- name: Run Bandit
run: |
bandit -r . -x ./venv,./.venv,./tests -s B101,B404,B601,B603 \
-f sarif -o bandit-results.sarif --exit-zero
jq -r '"Bandit: \(.runs[0].results | length) finding(s)",
(.runs[0].results[]
| " \(.level)\t\(.ruleId)\t"
+ "\(.locations[0].physicalLocation.artifactLocation.uri)"
+ ":\(.locations[0].physicalLocation.region.startLine)\t"
+ "\(.message.text | split("\n")[0])")' \
bandit-results.sarifOutput, from actually running the generated step:
Bandit: 1 finding(s)
note B607 roles/postgresql/files/provision_db.py:34 Starting a process with a partial executable path
It degrades to Bandit: 0 finding(s) with no jq error when there is nothing to report, which is the case on this repo. Fixed in 810ec5e.
Three points from @mramotowski on #6, applied to both repos so the two stay identical. Drop the cross-repo reference in the ansible-lint comment. Justifying a skip list in one repo by citing another repo's finding count was noise; each file now carries only its own numbers, measured fresh. Note that `schedule` only fires from the default branch. Correct, and worth saying in the file rather than leaving as a trap: the weekly crons stay dormant until these workflows also land on `main`. The `push` and `pull_request` triggers are unaffected. Added to all four scheduled workflows. Scan once in Bandit instead of twice. The second invocation existed only to print something human-readable, and re-scanned the whole tree to do it. The summary is now rendered with `jq` from the SARIF the first scan already wrote, which also keeps the two outputs from ever disagreeing: Bandit: 1 finding(s) note B607 roles/postgresql/files/provision_db.py:34 Starting a process with a partial executable path No change to what any scan reports. Verified by extracting the generated Bandit step from the YAML and running it verbatim under `bash -e` in both repos -- 1 finding in ai-solutions, 0 in inference, and the summary degrades cleanly to "0 finding(s)". ansible-lint still reports 5 and 4. actionlint clean.
The counts in the skip-list rationale were measured locally, where ansible-lint silently skips schema validation because it cannot fetch the JSON schemas. Two schema[meta] findings therefore only appear in CI: roles/langfuse and roles/litellm are the only role metas declaring galaxy_info, and the schema requires 'author' whenever it is present. Baseline is 501 findings unfiltered and 6 substantive, not 499 and 4. name[casing] is unchanged at 350. ai-solutions is unaffected -- schema is an unskippable rule, so CI's filtered run would have surfaced any schema finding there, and it reported none. The two schema[meta] findings are left unfixed, consistent with every other genuine finding this PR reports rather than resolves.
Three points from @mramotowski on intel/enterprise-inference#6, applied to both repos so the two stay identical. Drop the cross-repo reference in the ansible-lint comment. Justifying a skip list in one repo by citing another repo's finding count was noise; each file now carries only its own numbers, measured fresh. Note that `schedule` only fires from the default branch. Correct, and worth saying in the file rather than leaving as a trap: the weekly crons stay dormant until these workflows also land on `main`. The `push` and `pull_request` triggers are unaffected. Added to all four scheduled workflows. Scan once in Bandit instead of twice. The second invocation existed only to print something human-readable, and re-scanned the whole tree to do it. The summary is now rendered with `jq` from the SARIF the first scan already wrote, which also keeps the two outputs from ever disagreeing: Bandit: 1 finding(s) note B607 roles/postgresql/files/provision_db.py:34 Starting a process with a partial executable path No change to what any scan reports. Verified by extracting the generated Bandit step from the YAML and running it verbatim under `bash -e` in both repos -- 1 finding in ai-solutions, 0 in inference, and the summary degrades cleanly to "0 finding(s)". ansible-lint still reports 5 and 4. actionlint clean.
* Rename product to Intel® AI for Enterprise Solutions (#2) * docs: rename product to Intel® AI Enterprise Solutions * docs: clarify this release delivers the inference foundation for RAG/agentic services * docs: update solutions architecture diagram * docs: correct product name to Intel® AI for Enterprise Solutions --------- Co-authored-by: release-bot <release-bot@users.noreply.github.com> * docs: fix broken install command, dead anchor, and vague heading (#4) * docs: fix broken install command path and dead traditional-ml anchor * docs: fix dead traditional-ml anchor in deploy_models * docs: rename vague '30-second integration' heading to describe the steps --------- Co-authored-by: release-bot <release-bot@users.noreply.github.com> * ci: add pinned security scanning workflows Add SARIF-publishing security scans for the release branch, following the pattern already used across the intel org (harden-runner + per-tool workflow files + OpenSSF Scorecard): - trivy vulnerabilities, secrets, misconfig, licenses (filesystem) - bandit Python security findings - checkov Kubernetes / Actions / secrets IaC checks - shellcheck shell script correctness and quoting - ansible-lint Ansible role and playbook hygiene - scorecard OpenSSF supply-chain posture rating Every action is pinned to a full commit SHA and every tool to an exact version, so a given commit always scans the same way and version bumps are explicit, reviewable diffs. ShellCheck is fetched from its upstream release and checksum-verified rather than installed via apt, whose version drifts with the runner image. Each scan was run locally against the tree before landing, and two were retuned as a result: - checkov drops the `helm` framework. Checkov renders charts into a random temp dir and reports paths relative to it, so all 50 (ai-solutions) / 40 (inference) findings pointed at files that do not exist in the repo -- unanchorable in the Security tab and re-reported as new every run. They were also entirely in vendored upstream charts. Trivy's misconfig scanner covers chart misconfiguration with correct paths. - ansible-lint skips style-only rule families, which accounted for 1158 of 1163 findings in ai-solutions and 496 of 502 in inference. name[casing] alone fired 350 times against the deliberate `role | action | desc` naming convention. Jobs are soft-fail for the initial rollout: findings surface under Security -> Code scanning instead of blocking the branch, so the existing backlog can be triaged before any check is made required. CodeQL is intentionally not added here - it already runs via code scanning default setup on both repositories. * ci: eliminate false positives from the security scans Triage of every finding the scans produced, separating defects that cannot be true from ones worth a maintainer's time. Every remaining finding was checked by hand against the source and is real. ShellCheck (20 -> 18) Discovery now reads shebangs as well as *.sh suffixes; the extension-less `model-manager` was previously never scanned. Entry points are linted with -x plus a --source-path list so cross-file globals resolve. This repo has no script that another script sources, so the per-variable SC2034 handling the shared workflow applies to sourced helper scripts is inert here. Two annotations, no behaviour change: - es_auto_installer.sh: the commas in `sudo --preserve-env=a,b,c` are sudo's own list syntax, not array separators (SC2054). - probe-node-topology.sh: `# shellcheck shell=sh`. The file has no shebang because it is read with lookup('file', ...) and piped into a pod's shell, never executed directly (SC2148). It is clean as POSIX sh. Kept: MODEL_NAMESPACES is genuinely unset-and-unused in calculate_actual_infrastructure_cpus.sh, and reads like a missing exclusion; 11 SC2155 and 6 SC2207 are real. Bandit (4 -> 1) Skips B404 (importing subprocess is not a defect) and B603, which fires on the recommended argv-list form and cannot judge whether input is trusted. shell=True and friends (B602/B604/B605/B609) stay enabled. Severity is deliberately not filtered, since real tests such as B105 report LOW. Kept: B607, `psql` resolved through PATH in provision_db.py. Trivy (0 -> 0, plus a fixed threshold leak) `limit-severities-for-sarif` defaults to false, and unset it makes the action drop --severity for the SARIF run alone -- the table reported 0 findings while the uploaded SARIF carried LOW/MEDIUM ones the declared CRITICAL,HIGH threshold should have excluded. Now split in two: CVEs, secrets and licences at CRITICAL,HIGH with the limit applied; misconfiguration at every severity, because Trivy rates nearly every Kubernetes check LOW or MEDIUM and the same threshold would silence that scanner outright. Noise is controlled by scope instead -- vendored charts/ is skipped, matching the Checkov workflow. ansible-lint, Checkov No false positives. The 5 ansible-lint findings stand: `kubectl get nodes | grep -v ' Ready' | wc -l` reports 0 not-ready nodes when kubectl itself fails, which is the bug pipefail prevents. Verified locally against the pinned tool versions, under `bash -e` as Actions runs it. actionlint clean. * ci: address review feedback on the scanning workflows Three points from @mramotowski on intel/enterprise-inference#6, applied to both repos so the two stay identical. Drop the cross-repo reference in the ansible-lint comment. Justifying a skip list in one repo by citing another repo's finding count was noise; each file now carries only its own numbers, measured fresh. Note that `schedule` only fires from the default branch. Correct, and worth saying in the file rather than leaving as a trap: the weekly crons stay dormant until these workflows also land on `main`. The `push` and `pull_request` triggers are unaffected. Added to all four scheduled workflows. Scan once in Bandit instead of twice. The second invocation existed only to print something human-readable, and re-scanned the whole tree to do it. The summary is now rendered with `jq` from the SARIF the first scan already wrote, which also keeps the two outputs from ever disagreeing: Bandit: 1 finding(s) note B607 roles/postgresql/files/provision_db.py:34 Starting a process with a partial executable path No change to what any scan reports. Verified by extracting the generated Bandit step from the YAML and running it verbatim under `bash -e` in both repos -- 1 finding in ai-solutions, 0 in inference, and the summary degrades cleanly to "0 finding(s)". ansible-lint still reports 5 and 4. actionlint clean. * fix: pin the enterprise-inference clone to v1.0.0 config/repos.yaml tracked `main` on the external inference repo, so the tree the installer cloned changed underneath us with every upstream merge. Two installs from the same solutions commit could produce different role trees, which makes a deployment neither reproducible nor reviewable. Pin to the v1.0.0 tag instead. A bare commit SHA would be stronger, but `git clone --branch` accepts only branch and tag names: a SHA fails the clone and ensure_repos' `||` fallback then clones the default branch silently, which is strictly worse than tracking main openly. Using the tag needs no installer change at all. The field docs now state what the field accepts and call out the SHA trap. Verified by running the installer's real ensure_repos() against this config with es_auto_installer.sh unmodified: HEAD lands on the tagged commit, detached, `describe --exact-match` reports v1.0.0, roles/ is found and ANSIBLE_ROLES_PATH is correct. * ci: raise ShellCheck threshold to error to suppress style warnings --------- Co-authored-by: Mushtaq <mushtaq.ahmed@intel.com> Co-authored-by: release-bot <release-bot@users.noreply.github.com> Co-authored-by: sysadmin <sysadmin@localhost>
* Merge pull request #2 from Mushtaq-BGA/release-1.0 Docs: rename to Intel® AI for Enterprise Inference + release doc fixes * docs: brand component badge as Intel AI for Enterprise Solutions (#3) Co-authored-by: release-bot <release-bot@users.noreply.github.com> * docs: add Intel® trademark to component badge (#4) Co-authored-by: release-bot <release-bot@users.noreply.github.com> * ci: add pinned security scanning workflows Add SARIF-publishing security scans for the release branch, following the pattern already used across the intel org (harden-runner + per-tool workflow files + OpenSSF Scorecard): - trivy vulnerabilities, secrets, misconfig, licenses (filesystem) - bandit Python security findings - checkov Kubernetes / Actions / secrets IaC checks - shellcheck shell script correctness and quoting - ansible-lint Ansible role and playbook hygiene - scorecard OpenSSF supply-chain posture rating Every action is pinned to a full commit SHA and every tool to an exact version, so a given commit always scans the same way and version bumps are explicit, reviewable diffs. ShellCheck is fetched from its upstream release and checksum-verified rather than installed via apt, whose version drifts with the runner image. Each scan was run locally against the tree before landing, and two were retuned as a result: - checkov drops the `helm` framework. Checkov renders charts into a random temp dir and reports paths relative to it, so all 50 (ai-solutions) / 40 (inference) findings pointed at files that do not exist in the repo -- unanchorable in the Security tab and re-reported as new every run. They were also entirely in vendored upstream charts. Trivy's misconfig scanner covers chart misconfiguration with correct paths. - ansible-lint skips style-only rule families, which accounted for 1158 of 1163 findings in ai-solutions and 496 of 502 in inference. name[casing] alone fired 350 times against the deliberate `role | action | desc` naming convention. Jobs are soft-fail for the initial rollout: findings surface under Security -> Code scanning instead of blocking the branch, so the existing backlog can be triaged before any check is made required. CodeQL is intentionally not added here - it already runs via code scanning default setup on both repositories. * ci: eliminate false positives from the security scans Triage of every finding the scans produced, separating defects that cannot be true from ones worth a maintainer's time. Every remaining finding was checked by hand against the source and is real. ShellCheck (33 -> 7) Discovery now reads shebangs as well as *.sh suffixes: model_manager/model- manager is the largest script in the tree at 1429 lines and a *.sh glob never saw it. Entry points are linted with -x so the helper scripts they source are followed, plus a --source-path list -- the source statements are built from "$MM_LIB/..." which ShellCheck cannot resolve unaided, and without it 7 globals that model_manager/lib/ does set and model-manager does read looked unused. The helper scripts under model_manager/lib/ are linted on their own. ShellCheck sees one file at a time, so a global a helper sets for its caller reads as unused: 26 of the 31 SC2034 hits were that. Disabling SC2034 for those files was too blunt -- it also hid four globals that genuinely are written and never read anywhere. Each hit is now checked individually and suppressed only when another file demonstrably reads that variable, limited to SHOUTY_CASE names of 3+ characters since a short lowercase name such as a loop's `i` collides textually with unrelated files. Kept, all verified by grepping the whole tree: - config.sh:266 RUNTIME_DEVICE, parsed from the runtime JSON beside five siblings that are all consumed, then never read -- cpu/gpu selection is read and dropped. - nri.sh:82,83 NRI_ADVANCED_MODE and NRI_HIDE_HT, both named in the function's own "Sets:" header comment as outputs, neither consumed. The Python side reads the MM_NRI_HIDE_HT environment variable, not this. - nri.sh:241 NRI_TARGET_NODE, set beside NRI_POLICY_NAME which is used. - helpers.sh:244 `local i` in a `for i in $(seq 1 24)` retry loop whose body never uses it. - helpers.sh:88, routing.sh:288 SC2155. No file under model_manager/ was modified. Bandit (0 -> 0) Skips B404 (importing subprocess is not a defect) and B603, which fires on the recommended argv-list form and cannot judge whether input is trusted. shell=True and friends (B602/B604/B605/B609) stay enabled. Severity is deliberately not filtered, since real tests such as B105 report LOW. Trivy (17 -> 1) `limit-severities-for-sarif` defaults to false, and unset it makes the action drop --severity for the SARIF run alone -- the table reported 0 findings while the uploaded SARIF carried all 17 at LOW/MEDIUM, every one below the declared CRITICAL,HIGH threshold. Now split in two: CVEs, secrets and licences at CRITICAL,HIGH with the limit applied; misconfiguration at every severity, because Trivy rates nearly every Kubernetes check LOW or MEDIUM and the same threshold would silence that scanner outright. Noise is controlled by scope instead -- vendored charts/litellm-helm accounted for 16 of the 17 and is now skipped, matching the Checkov workflow. Kept: KSV-0106 in roles/llm_services/files/runtimes/vllm-runtime.yaml, the one finding in code this repo owns. ansible-lint, Checkov No false positives; the 4 ansible-lint findings stand. Verified locally against the pinned tool versions, under `bash -e` as Actions runs it. actionlint clean. * ci: address review feedback on the scanning workflows Three points from @mramotowski on #6, applied to both repos so the two stay identical. Drop the cross-repo reference in the ansible-lint comment. Justifying a skip list in one repo by citing another repo's finding count was noise; each file now carries only its own numbers, measured fresh. Note that `schedule` only fires from the default branch. Correct, and worth saying in the file rather than leaving as a trap: the weekly crons stay dormant until these workflows also land on `main`. The `push` and `pull_request` triggers are unaffected. Added to all four scheduled workflows. Scan once in Bandit instead of twice. The second invocation existed only to print something human-readable, and re-scanned the whole tree to do it. The summary is now rendered with `jq` from the SARIF the first scan already wrote, which also keeps the two outputs from ever disagreeing: Bandit: 1 finding(s) note B607 roles/postgresql/files/provision_db.py:34 Starting a process with a partial executable path No change to what any scan reports. Verified by extracting the generated Bandit step from the YAML and running it verbatim under `bash -e` in both repos -- 1 finding in ai-solutions, 0 in inference, and the summary degrades cleanly to "0 finding(s)". ansible-lint still reports 5 and 4. actionlint clean. * ci: correct ansible-lint baseline counts in comment The counts in the skip-list rationale were measured locally, where ansible-lint silently skips schema validation because it cannot fetch the JSON schemas. Two schema[meta] findings therefore only appear in CI: roles/langfuse and roles/litellm are the only role metas declaring galaxy_info, and the schema requires 'author' whenever it is present. Baseline is 501 findings unfiltered and 6 substantive, not 499 and 4. name[casing] is unchanged at 350. ai-solutions is unaffected -- schema is an unskippable rule, so CI's filtered run would have surfaced any schema finding there, and it reported none. The two schema[meta] findings are left unfixed, consistent with every other genuine finding this PR reports rather than resolves. * ci: raise ShellCheck threshold to error to suppress style warnings --------- Co-authored-by: Mushtaq <mushtaq.ahmed@intel.com> Co-authored-by: release-bot <release-bot@users.noreply.github.com> Co-authored-by: sysadmin <sysadmin@localhost>
* Merge pull request #2 from Mushtaq-BGA/release-1.0 Docs: rename to Intel® AI for Enterprise Inference + release doc fixes * docs: brand component badge as Intel AI for Enterprise Solutions (#3) Co-authored-by: release-bot <release-bot@users.noreply.github.com> * docs: add Intel® trademark to component badge (#4) Co-authored-by: release-bot <release-bot@users.noreply.github.com> * ci: add pinned security scanning workflows Add SARIF-publishing security scans for the release branch, following the pattern already used across the intel org (harden-runner + per-tool workflow files + OpenSSF Scorecard): - trivy vulnerabilities, secrets, misconfig, licenses (filesystem) - bandit Python security findings - checkov Kubernetes / Actions / secrets IaC checks - shellcheck shell script correctness and quoting - ansible-lint Ansible role and playbook hygiene - scorecard OpenSSF supply-chain posture rating Every action is pinned to a full commit SHA and every tool to an exact version, so a given commit always scans the same way and version bumps are explicit, reviewable diffs. ShellCheck is fetched from its upstream release and checksum-verified rather than installed via apt, whose version drifts with the runner image. Each scan was run locally against the tree before landing, and two were retuned as a result: - checkov drops the `helm` framework. Checkov renders charts into a random temp dir and reports paths relative to it, so all 50 (ai-solutions) / 40 (inference) findings pointed at files that do not exist in the repo -- unanchorable in the Security tab and re-reported as new every run. They were also entirely in vendored upstream charts. Trivy's misconfig scanner covers chart misconfiguration with correct paths. - ansible-lint skips style-only rule families, which accounted for 1158 of 1163 findings in ai-solutions and 496 of 502 in inference. name[casing] alone fired 350 times against the deliberate `role | action | desc` naming convention. Jobs are soft-fail for the initial rollout: findings surface under Security -> Code scanning instead of blocking the branch, so the existing backlog can be triaged before any check is made required. CodeQL is intentionally not added here - it already runs via code scanning default setup on both repositories. * ci: eliminate false positives from the security scans Triage of every finding the scans produced, separating defects that cannot be true from ones worth a maintainer's time. Every remaining finding was checked by hand against the source and is real. ShellCheck (33 -> 7) Discovery now reads shebangs as well as *.sh suffixes: model_manager/model- manager is the largest script in the tree at 1429 lines and a *.sh glob never saw it. Entry points are linted with -x so the helper scripts they source are followed, plus a --source-path list -- the source statements are built from "$MM_LIB/..." which ShellCheck cannot resolve unaided, and without it 7 globals that model_manager/lib/ does set and model-manager does read looked unused. The helper scripts under model_manager/lib/ are linted on their own. ShellCheck sees one file at a time, so a global a helper sets for its caller reads as unused: 26 of the 31 SC2034 hits were that. Disabling SC2034 for those files was too blunt -- it also hid four globals that genuinely are written and never read anywhere. Each hit is now checked individually and suppressed only when another file demonstrably reads that variable, limited to SHOUTY_CASE names of 3+ characters since a short lowercase name such as a loop's `i` collides textually with unrelated files. Kept, all verified by grepping the whole tree: - config.sh:266 RUNTIME_DEVICE, parsed from the runtime JSON beside five siblings that are all consumed, then never read -- cpu/gpu selection is read and dropped. - nri.sh:82,83 NRI_ADVANCED_MODE and NRI_HIDE_HT, both named in the function's own "Sets:" header comment as outputs, neither consumed. The Python side reads the MM_NRI_HIDE_HT environment variable, not this. - nri.sh:241 NRI_TARGET_NODE, set beside NRI_POLICY_NAME which is used. - helpers.sh:244 `local i` in a `for i in $(seq 1 24)` retry loop whose body never uses it. - helpers.sh:88, routing.sh:288 SC2155. No file under model_manager/ was modified. Bandit (0 -> 0) Skips B404 (importing subprocess is not a defect) and B603, which fires on the recommended argv-list form and cannot judge whether input is trusted. shell=True and friends (B602/B604/B605/B609) stay enabled. Severity is deliberately not filtered, since real tests such as B105 report LOW. Trivy (17 -> 1) `limit-severities-for-sarif` defaults to false, and unset it makes the action drop --severity for the SARIF run alone -- the table reported 0 findings while the uploaded SARIF carried all 17 at LOW/MEDIUM, every one below the declared CRITICAL,HIGH threshold. Now split in two: CVEs, secrets and licences at CRITICAL,HIGH with the limit applied; misconfiguration at every severity, because Trivy rates nearly every Kubernetes check LOW or MEDIUM and the same threshold would silence that scanner outright. Noise is controlled by scope instead -- vendored charts/litellm-helm accounted for 16 of the 17 and is now skipped, matching the Checkov workflow. Kept: KSV-0106 in roles/llm_services/files/runtimes/vllm-runtime.yaml, the one finding in code this repo owns. ansible-lint, Checkov No false positives; the 4 ansible-lint findings stand. Verified locally against the pinned tool versions, under `bash -e` as Actions runs it. actionlint clean. * ci: address review feedback on the scanning workflows Three points from @mramotowski on #6, applied to both repos so the two stay identical. Drop the cross-repo reference in the ansible-lint comment. Justifying a skip list in one repo by citing another repo's finding count was noise; each file now carries only its own numbers, measured fresh. Note that `schedule` only fires from the default branch. Correct, and worth saying in the file rather than leaving as a trap: the weekly crons stay dormant until these workflows also land on `main`. The `push` and `pull_request` triggers are unaffected. Added to all four scheduled workflows. Scan once in Bandit instead of twice. The second invocation existed only to print something human-readable, and re-scanned the whole tree to do it. The summary is now rendered with `jq` from the SARIF the first scan already wrote, which also keeps the two outputs from ever disagreeing: Bandit: 1 finding(s) note B607 roles/postgresql/files/provision_db.py:34 Starting a process with a partial executable path No change to what any scan reports. Verified by extracting the generated Bandit step from the YAML and running it verbatim under `bash -e` in both repos -- 1 finding in ai-solutions, 0 in inference, and the summary degrades cleanly to "0 finding(s)". ansible-lint still reports 5 and 4. actionlint clean. * ci: correct ansible-lint baseline counts in comment The counts in the skip-list rationale were measured locally, where ansible-lint silently skips schema validation because it cannot fetch the JSON schemas. Two schema[meta] findings therefore only appear in CI: roles/langfuse and roles/litellm are the only role metas declaring galaxy_info, and the schema requires 'author' whenever it is present. Baseline is 501 findings unfiltered and 6 substantive, not 499 and 4. name[casing] is unchanged at 350. ai-solutions is unaffected -- schema is an unskippable rule, so CI's filtered run would have surfaced any schema finding there, and it reported none. The two schema[meta] findings are left unfixed, consistent with every other genuine finding this PR reports rather than resolves. * ci: raise ShellCheck threshold to error to suppress style warnings * Introduce changes for patch v1.0.1 (#8) * Release v1.0.1 Patch release on top of v1.0.0. - model-manager: deduplicate args by flag name in LLMInferenceService - model-manager: add --label flag for deployments - model-manager: undeploy --wait blocks until pods terminate - nfd: label namespace with PSS profile when enforce_pss is true - llm_services: provision the model-store PVC from the resolved storage backend Co-authored-by: jpiaseck <jakub.piasecki@intel.com> Co-authored-by: Ant Albersk <anna.alberska@intel.com> Co-authored-by: n-owi <mikolaj.nowicki@intel.com> Co-authored-by: sgurunat <gurunath.s@intel.com> * ci: match all release branches in workflow triggers Trigger the security-scan workflows on release-[0-9]+.[0-9]+ branches (e.g. release-1.0.1), not just release-1.0, and add the same branch filter to pull_request events. Co-authored-by: Maciej Ramotowski <maciej.ramotowski@intel.com> --------- Co-authored-by: jpiaseck <jakub.piasecki@intel.com> Co-authored-by: Ant Albersk <anna.alberska@intel.com> Co-authored-by: n-owi <mikolaj.nowicki@intel.com> Co-authored-by: sgurunat <gurunath.s@intel.com> Co-authored-by: Maciej Ramotowski <maciej.ramotowski@intel.com> --------- Co-authored-by: Mushtaq <mushtaq.ahmed@intel.com> Co-authored-by: release-bot <release-bot@users.noreply.github.com> Co-authored-by: sysadmin <sysadmin@localhost> Co-authored-by: jpiaseck <jakub.piasecki@intel.com> Co-authored-by: Ant Albersk <anna.alberska@intel.com> Co-authored-by: n-owi <mikolaj.nowicki@intel.com> Co-authored-by: sgurunat <gurunath.s@intel.com> Co-authored-by: Maciej Ramotowski <maciej.ramotowski@intel.com>
Summary
Adds security scanning to
release-1.0, following the pattern already used across theintelorg: per-tool workflow files,harden-runneras the first step, OpenSSF Scorecard, and actions pinned by commit SHA.trivy.ymlbandit.ymlcheckov.ymlshellcheck.ymlansible-lint.ymlscorecard.ymlEach tool uploads under its own
category:, so results don't overwrite one another.Pinning
Every action is pinned to a full commit SHA (tag in a trailing comment) and every tool to an exact version — no floating
latest/major tags.Actions:
harden-runnerv2.21.1 ·checkoutv7.0.1 ·setup-pythonv7.0.0 ·upload-artifactv4.6.2 ·trivy-actionv0.36.0 ·codeql-action/upload-sarifv4.37.9 ·scorecard-actionv2.4.4Tools: Python 3.12.14 · Bandit 1.9.4 · Checkov 3.3.16 · ansible-lint 26.8.0 · Trivy CLI v0.74.0 · ShellCheck v0.11.0 · runner
ubuntu-24.04The SHAs for
checkoutandtrivy-actionmatch those already in use inintel/MigTD,intel/cve-bin-toolandintel/intel-device-plugins-for-kubernetes.ShellCheck is fetched from its upstream release and checksum-verified rather than installed via
apt, whose version drifts with the runner image. Trivy's binary is pinned but its vulnerability DB is fetched fresh each run — intentional, since a scanner pinned to stale CVE data is worse than useless.Two tools were retuned after running them locally
Both scans were run against this tree before landing, which surfaced problems that would have made them useless in practice:
Checkov drops the
helmframework. It renders charts into a random temp dir and reports paths relative to that, so all findings pointed at files liketmppna2ze0v/charts/.../deployment.yaml— nonexistent in the repo, therefore unanchorable in the Security tab, and re-reported as brand-new on every run since the temp name changes. The findings were also entirely inside vendored upstream charts. Trivy's misconfig scanner covers chart misconfiguration with correct paths, so coverage isn't lost.charts/is skipped for the same reason.ansible-lint skips style-only rule families. Unfiltered it produced 502 findings here, of which just 6 were substantive —
name[casing]alone fired against the deliberaterole | action | descnaming convention. Skippingname,var-naming,fqcn,yaml,jinja,key-orderleaves exactly the correctness and command-shell findings.dependency-review.ymlis not included: this repo has no dependency manifest (norequirements.txt/pyproject.tomlanywhere), so the check would be a permanently-passing no-op. Worth adding the day one lands. Note this also means Trivy'svulnscanner has no Python manifest to resolve, so a green Trivy result should not be read as "no Python dependency risk" for this repo.Hardening
permissions: contents: read;security-events: writeonly where SARIF is uploaded.persist-credentials: falseon every checkout.concurrencygroups cancel superseded runs.Notes
exit-code: '0',--exit-zero,--soft-fail,continue-on-error). Nothing blocks the branch yet, so the backlog can be triaged before any check is made required.scorecard.ymltriggers on the default branch,branch_protection_ruleand a schedule, not on PRs — it grades the repository, not a diff. It will not appear in this PR's checks; verify it viaworkflow_dispatchafter merge.publish_results: truesends results to OpenSSF's public dataset (this is what the other 24 Scorecard-usingintelrepos do). Set it tofalseto keep results in the Security tab only.Branch-Protectioncheck needs an admin-scoped token and will read as inconclusive with the defaultGITHUB_TOKEN.Test plan
Verified locally before pushing:
actionlint1.7.12 +shellcheck0.11.0 clean across all 6 workflow files (includes linting therun:blocks).trivy-actioninputs confirmed to exist inaction.yamlat the pinned SHA.sha256sum --check --strict.actions/python-versionsmanifest with a 24.04 linux-x64 build.results.sarifproduced, 0 findings with 172 checks passing.schema, 3no-changed-when, 1risky-shell-pipe).release-1.0with no base drift; commit touches only.github/workflows/.github.com/koalaman/shellcheck/releases.To confirm on CI:
scorecard.ymldispatched successfully after merge.Second commit: false-positive triage (
813d996)Every finding the scans produced was triaged by hand, separating defects that
cannot be true from ones worth a maintainer's time.
ShellCheck. Discovery now reads shebangs as well as
*.shsuffixes:model_manager/model-manageris the largest script in the tree at 1429 linesand a
*.shglob never saw it. Entry points are linted with-xso the helperscripts they source are followed, plus a
--source-pathlist — the sourcestatements are built from
"$MM_LIB/...", which ShellCheck cannot resolveunaided, and without it 7 globals that
model_manager/lib/does set andmodel-managerdoes read looked unused.The helper scripts under
model_manager/lib/are linted on their own.ShellCheck sees one file at a time, so a global a helper sets for its caller
reads as unused: 26 of the 31
SC2034hits were that. DisablingSC2034forthose files was too blunt — it also hid four globals that genuinely are written
and never read anywhere. Each hit is now checked individually and suppressed
only when another file demonstrably reads that variable, limited to
SHOUTY_CASEnames of 3+ characters, since a short lowercase name such as aloop's
icollides textually with unrelated files.No file under
model_manager/was modified.Trivy — a real defect fixed.
limit-severities-for-sarifdefaults tofalse, and left unset the action drops
--severityfor the SARIF run alone: thejob's table printed 0 findings while the uploaded SARIF carried all 17 at
LOW/MEDIUM, every one below the declared
CRITICAL,HIGHthreshold. The Securitytab was silently ignoring the configured policy. Now split in two — CVEs,
secrets and licences at
CRITICAL,HIGHwith the limit applied, andmisconfiguration at every severity, because Trivy rates nearly every Kubernetes
check LOW or MEDIUM and the same threshold would silence that scanner outright.
Noise is controlled by scope instead: vendored
charts/litellm-helmaccountedfor 16 of the 17 and is now skipped, matching Checkov.
Bandit. Skips
B404(importingsubprocessis not a defect) andB603,which fires on the recommended argv-list form and cannot judge whether the
input is trusted.
shell=Trueand friends (B602/B604/B605/B609) stayenabled. Severity is deliberately not filtered, since real tests such as
B105report LOW.Findings deliberately kept — each verified against the whole tree
config.sh:266—RUNTIME_DEVICEis parsed from the runtime JSON(
.device // "cpu") beside five siblings that are all consumed, thennever read. Device selection is read and dropped.
nri.sh:82,83—NRI_ADVANCED_MODEandNRI_HIDE_HTare both named in thefunction's own
Sets:header comment as outputs, and neither is consumed.The Python side reads the
MM_NRI_HIDE_HTenvironment variable, not these.nri.sh:241—NRI_TARGET_NODE, set besideNRI_POLICY_NAME, which isused.
helpers.sh:244—local iin afor i in $(seq 1 24)retry loop whose bodynever uses it.
helpers.sh:88,routing.sh:288—SC2155.KSV-0106inroles/llm_services/files/runtimes/vllm-runtime.yaml, the oneTrivy finding in code this repo owns.
These are reported, not fixed — all scans are soft-fail, so they surface without
blocking.
Verified on CI
8 script(s): 3 entry point(s), 5 sourced helper(s)and 7 findings — matching the local run exactly.
trivy-misconfign=1(down from
trivyn=17),trivy-vulnn=0,banditn=0,checkovn=0.Known gaps, left open by choice
charts/is now scanned by neither Checkov nor Trivy. Worthrevisiting if those charts are ever forked rather than vendored.
vulnscanner has nothing toresolve — a green Trivy result must not be read as "no Python dependency
risk". This is also why there is no
dependency-reviewworkflow here..github/dependabot.yml, which scores Scorecard'sDependency-Update-Toolcheck at 0.
scorecard.ymldoes not run on pull requests and still needs aworkflow_dispatchafter merge to confirm it works.