12 Green CI Jobs, 422 Security Findings: Lessons from Building an Observability Platform
> An engineering journey through Python, React, Docker, GitHub Actions, observability, and software supply chain security.
Introduction
Does a green CI pipeline mean your application is secure?
That question became much more meaningful while developing InfraPulse, a monitoring and observability platform I'm building as a hands-on engineering and technical portfolio project.
Throughout development, I implemented automated tests, infrastructure validation, PostgreSQL integration, end-to-end testing, observability checks, and software supply chain security controls.
Eventually, the project reached 12 successful GitHub Actions jobs, including dependency integrity checks, secret detection, and Docker image validation.
Everything looked promising.
Then I introduced dedicated vulnerability scanning.
The scanners reported 422 findings, including one classified as critical and 95 as high severity.
That doesn't mean InfraPulse has 422 exploitable vulnerabilities. It means the scanners produced 422 findings that still require analysis, correlation, and classification.
That distinction matters.
In this article, I'll share how the project evolved, the engineering decisions behind its architecture, and what I learned about integrating security into the development lifecycle.
---
1. InfraPulse: From an Application to an Engineering Platform
InfraPulse started with a relatively straightforward goal: build an application capable of monitoring services and endpoints, recording results, and exposing operational information.
However, I didn't want to build just another functional dashboard.
I wanted a project that would allow me to practice engineering approaches commonly used in professional DevOps, SRE, and Platform Engineering environments.
That meant answering questions such as:
- How can I prevent backend changes from breaking frontend API contracts?
- How can I validate real application behavior against PostgreSQL?
- How should observability behave when an infrastructure component fails?
- How can I verify that Docker images contain the expected artifacts?
- How can I prevent dependencies and base images from changing silently?
- How can security be introduced without making CI unreliable?
These questions directly influenced the architecture.
Technology Stack
| Layer | Technologies |
|---|---|
| Backend | Python, FastAPI, SQLAlchemy, Alembic |
| Frontend | React, TypeScript, Vite, Mantine |
| Database | PostgreSQL 17 |
| Containers | Docker, Docker Compose, Nginx |
| CI/CD | GitHub Actions |
| Testing | Pytest, Vitest, Playwright |
| Metrics | Prometheus |
| Distributed tracing | OpenTelemetry, Tempo |
| Visualization | Grafana |
| Alerting | Alertmanager |
| Security | Gitleaks, integrity policies, pip-audit, npm audit, Trivy |
An important architectural decision was to keep components organized around clear responsibilities.
This made it possible to validate individual layers independently while also testing their integrated behavior.
2. The First Challenge: Building Trust in CI
Creating a GitHub Actions workflow that runs tests and displays a green checkmark is relatively straightforward.
Making sure that green checkmark represents meaningful verification is much harder.
InfraPulse's CI pipeline evolved incrementally.
I started with backend quality checks and unit tests.
Then I expanded coverage to include frontend validation, PostgreSQL integration, API contracts, end-to-end tests, and observability integration.
The next step was validating the Docker images produced by the project.
Finally, I introduced foundational software supply chain security controls.
The 12-Job CI Pipeline
In the reference remote execution, all 12 jobs passed:
| Job | Responsibility |
|---|---|
| backend-quality | Backend static quality checks |
| backend-unit | Unit tests |
| api-contract | OpenAPI contract validation |
| frontend-quality | Frontend quality checks |
| frontend-tests | Automated frontend tests |
| frontend-build | Frontend production build |
| backend-postgres | PostgreSQL integration tests |
| playwright-e2e | End-to-end tests |
| observability-integration | Real observability integration scenarios |
| docker-build | Docker builds and image validation |
| security-foundations | Foundational security controls |
| ci-required | Mandatory aggregate CI gate |
The ci-required job plays a particularly important role.
It verifies the results of all mandatory validation jobs.
This prevents the pipeline from being considered successful when a required job fails, is canceled, or is unexpectedly skipped.
A reliable CI gate must validate not only application behavior but also the integrity of the validation process itself.
All 12 jobs passed in the reference execution.
Figure 1 — GitHub Actions CI Results
!Figure 1 — InfraPulse GitHub Actions pipeline showing 12 successful jobs
Figure 1. InfraPulse CI pipeline: 12 successful jobs, covering quality, integration, end-to-end testing, observability, Docker image validation, and foundational security controls. Screenshot from a private repository.
3. Testing Observability Means Introducing Failure
One of the most interesting lessons was realizing that instrumenting an application is not the same as validating its observability.
Adding metrics, logs, and traces is only the beginning.
The real challenge is understanding how these signals behave when infrastructure components become unavailable.
In InfraPulse, I implemented integration scenarios involving the OpenTelemetry Collector.
The tests cover three situations:
Normal operation: The application runs while the Collector is available.
Outage: The Collector becomes unavailable.
Recovery: The Collector returns, and the system's behavior is observed again.
These scenarios help validate how the application and its instrumentation behave under changing operational conditions.
All three scenarios passed during the reference remote execution.
This reinforced an important SRE principle:
Observability should be tested under failure conditions, not merely demonstrated through dashboards.
Of course, three successful scenarios do not prove complete system resilience.
They provide evidence about specific behaviors under controlled conditions.
And that is precisely what good engineering validation should do.
4. Docker: A Successful Build Is Not Enough
Another important milestone was improving Docker image validation.
At first, it might seem sufficient to execute docker build and confirm that the command exits successfully.
But that leaves several questions unanswered.
Does the image contain the correct files?
Does the generated frontend match the expected OpenAPI contract?
Does the worker shut down gracefully?
Can Nginx serve the expected assets?
Are the runtime configurations behaving correctly?
To address these questions, I expanded the pipeline with image inspections and smoke tests.
OpenAPI Contract and Frontend Consistency
The frontend uses TypeScript types generated from the OpenAPI contract.
During image validation, the generated types extracted from the frontend build were compared byte-for-byte against an independently generated reference.
Both used the contract from the same CI execution and a pinned generator version.
The verification did not merely check whether files existed or contained data.
It compared their complete contents.
This reduces the risk of shipping a frontend built with types that differ from the expected API contract.
Runtime Behavior
The image validation process also checked:
- The API /health endpoint returning HTTP 200.
- Readiness returning HTTP 503 when the database was unavailable, as expected in that test scenario.
- Worker startup and graceful shutdown using SIGTERM.
- Nginx configuration, health checks, and static assets.
- Docker image layer inspection.
- Cleanup of temporary validation resources.
These checks do not replace production testing.
However, they provide stronger evidence than a successful image build alone.
5. When DevSecOps Became Part of the Architecture
As the pipeline matured, another concern became increasingly important: the integrity of the components used to build and run the software.
An application can pass every functional test while still depending on vulnerable packages, mutable container images, or insufficiently controlled build tools.
That realization led me to introduce a dedicated software supply chain security phase.
The first step focused on foundational controls.
Secret Detection
I integrated Gitleaks to scan the repository history and working checkout for potentially exposed secrets.
Beyond running the scanner, I added synthetic positive and negative test fixtures, operational failure handling, and sanitized output.
The goal wasn't simply to execute a security tool.
It was to verify that the tool detected known test cases and that operational failures could not silently become successful results.
Python Dependency Integrity
Initially, the project contained 49 pinned Python requirements without package hashes.
Pinning dependency versions improves predictability, but it does not independently verify the integrity of the downloaded artifacts.
I introduced hash-verified installations and regression tests that deliberately reject tampered hashes.
Build and bootstrap tools were also version-pinned.
Docker Images Pinned by Digest
Another improvement involved replacing base-image references that relied only on mutable tags with digest-pinned references.
A tag can point to different content over time.
A digest identifies specific content, helping prevent silent changes to the image base.
This improves reproducibility but introduces an additional responsibility: maintaining controlled updates so security fixes can still be incorporated.
GitHub Actions Security
The workflow security policies also covered:
- GitHub Actions pinned to full commit SHAs.
- Least-privilege workflow permissions.
- Checkout without unnecessary credential persistence.
- Automated policies designed to detect regressions in these controls.
This work introduced the mandatory security-foundations job.
The reference CI execution passed all blocking foundational security policies with zero violations.
But one question remained:
Were the dependencies and container images free from known vulnerabilities?
6. The Turning Point: 422 Vulnerability Findings
After establishing foundational security controls, I started a dedicated vulnerability discovery phase.
I used three tools:
- pip-audit for Python dependencies.
- npm audit for frontend dependencies.
- Trivy for Docker image vulnerability scanning.
The results were significant.
| Target | Findings |
|---|---:|
| Python — pip-audit 2.10.1 | 2 advisories |
| Frontend — npm audit 11.16.0 | 15 occurrences across 14 packages |
| Backend image — Trivy 0.76.1 | 176 occurrences |
| Nginx image — Trivy | 229 occurrences |
| Total | 422 occurrences |
Figure 2 — Sanitized Vulnerability Audit Summary
!Figure 2 — Sanitized InfraPulse vulnerability audit summary showing 422 findings
Figure 2. Sanitized local vulnerability discovery summary. The 422 findings are scanner occurrences, not unique CVEs or confirmed exploitable vulnerabilities.
The consolidated severity distribution was:
| Reported severity | Occurrences |
|---|---:|
| Critical | 1 |
| High | 95 |
| Medium | 143 |
| Moderate | 15 |
| Low | 159 |
| Unknown | 9 |
These numbers require careful interpretation.
422 findings do not mean 422 unique CVEs, and they certainly do not prove 422 exploitable vulnerabilities.
The same advisory may appear across multiple images, packages, or scanning contexts.
Different scanners may also use different severity classifications and remediation information.
Furthermore, finding a vulnerable package inside a container image does not automatically demonstrate that the affected functionality is reachable or exploitable through the application.
A Concrete Example
Trivy flagged CVE-2026-111497 in brotli-libs version 1.2.0-r0, reporting 1.2.1-r0 as the fixed version.
The Python audit also reported advisories affecting setuptools, with fixed versions indicated by the scanner.
These findings deserve attention, but they still require technical triage.
Important questions include:
- Is the affected component present in the production runtime or only during the build?
- Is a fixed version available and compatible?
- Has the operating system distribution already applied a security backport?
- Is the vulnerable functionality actually exposed?
- Could the proposed update introduce regressions?
- Is the same underlying issue being counted across multiple targets?
At the time of this analysis, those questions had not yet been fully resolved.
Therefore, the vulnerability discovery phase was initially treated as informational, rather than immediately turning every finding into a blocking CI failure.
This was a deliberate engineering decision.
7. Why Not Block CI Immediately?
It may sound contradictory to identify a critical-severity finding without immediately failing the entire pipeline.
But detecting a potential vulnerability and designing a reliable blocking policy are two different activities.
A useful security policy must account for evidence quality, applicability, remediation availability, and scanner behavior.
Otherwise, CI can become unstable, and teams may start introducing broad exceptions simply to restore a green pipeline.
This does not mean ignoring critical findings.
It means prioritizing their investigation and defining verifiable remediation criteria.
The next stage of InfraPulse will focus on classifying findings, deduplicating occurrences, assessing applicability, and establishing security policies with justified, time-limited exceptions.
It is also important to distinguish between different audit outcomes:
PASS: The check executed successfully and its acceptance criteria were satisfied.
FINDINGS: The audit completed and identified issues requiring evaluation.
SCANNER_ERROR: The scanner failed to execute or collect results correctly.
NOT_RUN: The audit was not executed.
A scanner error must never be interpreted as evidence that no vulnerabilities exist.
8. What I've Learned So Far
Building InfraPulse has reinforced several lessons that I consider especially relevant to DevOps and SRE.
First: software quality is built in layers.
Unit tests, integration tests, E2E tests, image validation, and security checks answer different questions. None completely replaces the others.
Second: pipelines should produce evidence.
A green status is useful, but engineering confidence also depends on knowing what was executed, which artifacts were tested, and which limitations remain.
Third: reproducibility and security are related, but they are not the same thing.
Hashes and digests help control the content being used. They do not independently guarantee that the content is vulnerability-free.
Fourth: observability must include failure scenarios.
A working dashboard under normal conditions is valuable, but outage and recovery tests provide additional evidence about operational behavior.
Fifth: automating detection does not automate engineering judgment.
Scanners identify potential problems. Determining applicability, priority, and remediation requires context.
Perhaps the most important lesson is that security should not be treated as a separate activity performed only at the end of development.
It should evolve alongside architecture, testing, infrastructure, and delivery practices.
Conclusion
InfraPulse began as a monitoring project and evolved into a practical exercise in software engineering, reliability, and security.
Reaching 12 successful GitHub Actions jobs was an important milestone.
But discovering 422 vulnerability findings demonstrated that a mature pipeline does not mean the security work is finished.
Instead, it can provide the foundation needed to identify and investigate additional risks.
The next challenge is not simply making the scanners report zero findings.
It is building a security policy capable of distinguishing meaningful risks, prioritizing remediation, and preserving a reliable delivery process.
In Part 2, I plan to explore vulnerability triage, deduplication, applicability analysis, blocking policies, and auditable exceptions.
A green pipeline is evidence of engineering quality. It is not a certificate of zero risk.
---
References and Tools
- GitHub Actions Documentation
- OWASP Software Component Verification Standard
- Gitleaks
- pip-audit
- npm audit
- Trivy
- CVE-2026-111497 — Official CVE Record
- OpenTelemetry
- SLSA — Supply-chain Levels for Software Artifacts
This article documents an ongoing engineering project. Vulnerability scan results represent a specific point-in-time assessment, not a definitive statement about exploitability or overall application security. Package and remediation versions cited in the case study reflect the local scanner output.
InfraPulse is currently maintained in a private repository. The screenshots included in this article are sanitized excerpts of the project's validation results.
Unless your package is huge, your advisory waits
---
title: "Unless your package is huge, your advisory waits"
published: true
description: "It's now taking months for Dependabot and npm audit to flag known vulnerabilities, unless the package is one of the giants."
tags: "python, opensource, security, github"
canonical_url: "blog.peng.ly"
cover_image: "blog.peng.ly"
ai_assisted: true
---
On 11 July, Weblate published six security advisories on its GitHub repo, each naming the fixed version, 2026.7. Two made it into GitHub's Advisory Database seven weeks later. The other four still haven't, even though GitHub itself published CVEs for them on 26 August.
That matters because Dependabot only alerts on advisories GitHub has reviewed into that database. Until then, nobody whose Dependabot watches Weblate hears a thing. GitHub's docs still say review "can take up to 72 hours", and in spring that was about right: Weblate's earlier advisories this year went in within a day or so.
Last week I wrote about the same wait for CVEs that reach GitHub through NVD. This time it's advisories maintainers publish themselves, and I wanted to know who's doing the waiting.
The big ones get through
I picked 250 projects that had an npm or PyPI advisory reviewed by GitHub in 2025, pulled every advisory they've published this year (1,808 of them), and checked each one against GitHub's database on 26 October. Then I sorted them by how often the package gets downloaded in a month.
!Advisories reviewed within 30 days of publishing
Advisories reviewed within 30 days of publishing (the data)
_By the month the maintainer published it, and the package's monthly downloads. Each cell is 11
to 100 advisories from 7 to 21 projects, and September only counts ones published before the
27th, so every one's had 30 days_
Up to March, nearly everything was in the database within a few days, whoever you were. April and May slowed to about a week, still for everyone. Then from June the smaller packages start falling behind, and from July it's a cliff. Packages with over 10 million downloads a month got 93% of their June to August advisories reviewed within 30 days. Under 100k it was 35%, and half of them are still missing now, two to four months on.
A few projects publish dozens at once (Flowise did 33 in July), which can swing a month on its own. Counting projects instead tells the same story, though. Of the 17 with August advisories on packages under 100k, one got anything reviewed within 30 days.
Not first come, first served
October's even starker. Of the advisories published this month for packages over 10 million downloads, 84 of 143 are reviewed already. Half of those are Pillow's batch from last week, all 41 in within a week. For everything smaller, it's 12 of 147, while 55 advisories on smaller packages from before August are still waiting.
When I first ran this on 16 October, 301 of the advisories published January to September were waiting. GitHub's reviewed five of them since, two from Angular and one each from Hono, Next.js and devalue. All five are on packages with millions of downloads a month.
Criticals don't jump the queue either, even though GitHub's June post on the backlog said "critical issues prioritized". Over the summer, 48% of criticals were reviewed within 30 days, against 72% of lows.
Weblate's year
Weblate gets about 25,000 downloads a month on PyPI, so it's nowhere near the top. Here's how its advisories have gone this year:
- January to May 18 advisories, every one in GitHub's database within a week, most of them the next day.
- 1 June One more, reviewed after 37 days.
- 11 July Six more. Two reviewed 49 days later, the other four still waiting.
- 15 August Five more, none reviewed.
- 26 August GitHub, which hands out the CVE IDs for these, published CVEs for seven of the ones still waiting. NVD has them. GitHub's own database doesn't.
- 13 October Three more, two of them critical. Nothing yet, though that's only 13 days.
The 26 August bit threw me. I'd expected GitHub to publish a CVE when it reviewed the advisory, and it looks like the two aren't tied together any more. Across the 250 projects there are 35 advisories from January to September with a CVE GitHub's published and no entry in its database. Most of Weblate's are in OSV, but only against its git history, not the PyPI package, so osv-scanner on a pip install won't flag them.
Mine are waiting too
I'm writing this from experience (here's what I've reported). I recently found several quite severe vulnerabilities that are all fixed and public, and still waiting on GitHub's review, so Dependabot hasn't warned anyone about them:
- @libredb/studio - any signed-in user can run SQL on a database someone else has open, with that person's credentials (since 21 September)
- aedes - one MQTT packet, no login needed, crashes the broker for everyone connected to it (since 15 September)
- saxen - nested XML runs it out of memory in a way no try/catch can stop (since 21 September)
- comrak - a long list of email addresses in Markdown aborts the whole process when GitHub-style autolinks are on (since 6 September)
- social-auth-core - the VK app login lets you sign in as any VK user (since 18 September)
- pyhanko-certvalidator - changing the case of a hostname gets a certificate past a name constraint meant to block it (since 10 September)
- css_parser - a few dozen bytes of CSS can pin a CPU core for minutes (since 20 September)
- strnum - a long run of zeros in a number takes quadratic time, reachable through fast-xml-parser's defaults, on a package with 290 million downloads a month (since 14 August)
That said, others I reported made it into GitHub's Advisory Database relatively quickly, like RabbitMQ's Java client in 6 days, simple-git in 10, fast-uri in 11, smol-toml in 13, svgo in 15 and seroval in 16.
Stars help, a bit
Downloads aren't the whole picture. Some projects with lots of GitHub stars and smaller packages did fine: Open WebUI got all 62 of its summer advisories reviewed within 30 days, and NocoDB all 21. Others didn't. Backstage got 1 of 38 through in time and Directus 2 of 19. Sentry's five are all still waiting, three of them critical. Plenty of these are apps most people run from a Docker image rather than pip or npm, so their downloads undersell how many people actually use them.
It isn't that the big packages file tidier advisories. When I checked in mid-October, theirs were no more complete, and complete ones weren't reviewed any faster. GitHub's June post says it prioritises "based on several factors, including project impact and severity", and that it's looking at "additional risk signals for prioritization, such as package usage". From the outside it looks like usage counts for a lot already, but I can only see what comes out of the queue, not how it's run.
Doing the big ones first is fair enough, tbh. A bug in axios hits far more people than one in Weblate. But the 72 hours in the docs hasn't been true for months, and for a lot of packages "later" is turning into "not at all".
If yours isn't one of them
If you maintain something that isn't one of the giants, assume publishing the advisory won't reach anyone's Dependabot for a month or more, maybe not at all. Put it in the release notes in plain words, with the version to upgrade to, and post it wherever your users actually read. If it's on PyPI, you can also send it to PyPA's advisory database as a pull request, which osv-scanner reads. That's not instant either (a few have sat open for weeks), but they merged a couple last week.
If you depend on things that aren't huge, a clean Dependabot run means a lot less than it did in spring. For the ones you'd hate to miss, the repo's own Security tab lists every advisory the maintainers have published, reviewed or not, and watching its releases will catch the fix.
npm hasn't got a PyPA. There, the release notes are all you've got.
npm・pip・Composerで始めるSecuLens — 日本語レポートを読み、修正と再検査につなげる
脆弱性を検査できても、その結果をチームや顧客に説明する段階で手が止まることがあります。ターミナルの結果を転記し、重要度を並べ替え、部品のバージョンや検査に使った情報を別途添える。検査そのものに加えて、報告の準備にも作業が必要です。
自作のセキュリティ評価CLI SecuLens は、SBOM(ソフトウェア部品表)を読み、既知の脆弱性とライセンスポリシーを点検し、日本語のWordレポートと根拠のJSONをまとめて出力するために作りました。
npm・pip・Composerのうち、使い慣れた環境から導入できます。日本語の対話ウィザードもあるため、初回から長いオプションを覚える必要はありません。この記事では、導入の入口を示したあと、実際のレポートを「どこから読み、何を判断するか」の順で説明します。
今回の実測は、記事用に作ったローカルプロジェクトをTypeScript版で検査した結果です。検証日は2026年10月25日(日本時間)。Python版・PHP版は公開パッケージと公式の手順を確認して紹介しており、この検証で3実装を実行比較したものではありません。
まずは使い慣れたパッケージ管理ツールで導入する
3つすべてを入れる必要はありません。1つ選べば、対応する既存SBOMの脆弱性照合とレポート生成を始められます。 ソース解析も利用する場合は、コードの言語に合った実装を選びます。
実装
必要な環境
追加のソース解析
npm版
Node.js 22以降
JavaScript / TypeScript
pip版
Python 3.10以降
Python
Composer版
PHP 8.2以降、Composer 2、必要なPHP拡張
PHP
Node.jsを使っているならnpm
npm install -g seculens
seculens wizard --lang ja
npmパッケージから導入できます。後半の再現例では、検証時の1.0.0を使用しています。
Pythonを使っているならpip
Pythonの仮想環境を有効にした状態で実行します。
python -m pip install seculens
seculens wizard --lang ja
PyPIパッケージとして配布しています。Python版のSBOM検査・レポート出力のために、Node.jsを別途入れる必要はありません。
PHPを使っているならComposer
composer require --dev masahiroid/seculens
vendor/bin/seculens wizard --lang ja
Packagistパッケージから導入できます。必要なPHP拡張はcurl・dom・mbstring・xml・zipなどです。Composer版の実行コマンドは、プロジェクト内のvendor/bin/seculensになります。
初回は日本語ウィザードで設定する
上の起動コマンドから、既存SBOMの検査かSBOM生成かを選び、入力ファイル、脆弱性情報の取得方法、任意のポリシーやソース解析、出力先などを設定します。顧客名・検査対象名・作成者も指定でき、最後に条件を確認して実行できます。ウィザードの仕様
既存のSBOMがある場合は、それを読み込むところから始められます。SecuLensの導入だけで、どのプロジェクトのSBOMも生成できるわけではありません。 npm版の生成機能はnpm sbomを使い、Python版・PHP版の生成機能は別途Syftを必要とします。CIなどですでに生成したSBOMを使えば、生成ツールを追加する手順を省けます。
他のツールと比べて、どの作業が簡単になるのか
依存関係の既知の脆弱性を調べるだけなら、npmではnpm audit、Pythonではpip-audit、PHPではcomposer auditが直接的な入口です。SecuLensの利点は、検査後に説明できる報告を作り、再確認の材料をそろえる作業をまとめられることにあります。
やりたいこと
既存ツールで始める例
SecuLensでの進め方
使っている依存関係をまず検査する
npm audit / pip-audit / composer audit
SBOMを用意し、対応する部品をOSVと照合する
既存SBOMを検査する
TrivyにもSBOM検査コマンドがある
ウィザードで入力と取得方法を選ぶか、scanを実行する
読み手に渡す報告を整える
出力形式を選び、提出先に必要な説明や体裁を整える
日本語の主要見出し・要約、表紙、重要度順一覧、番号付き詳細をWordに出す
検査の根拠を保存する
利用ツールに応じて入力や情報源の保存方法を決める
使用したSBOM・OSVスナップショットと、ハッシュ付き結果を同じ出力先に保存する
各ツールの役割は、npm audit、pip-audit、Composer audit、TrivyのSBOM検査の公式資料を参照しています。これは機能と作業手順の比較で、今回それぞれを同条件で実行して、所要時間や検出精度を測った比較ではありません。
たとえば、すでにSBOMがあれば、npm版では次の1回の検査で報告まで出せます。
seculens scan sbom.json --fetch-osv --lang ja \
--report-style customer --output reports
ライセンスポリシーやソース解析を追加しなくても始められます。ライセンスの基準が未指定なら、その部分は要確認になります。慣れたらウィザードで選んだ条件をコマンドにし、同じ手順を繰り返せます。
TrivyにはコンテナーやOSなどを含む広い検査対象があります。SecuLensは、対応するnpm・PyPI・Packagistの部品を照合して報告する用途に絞っています。選定時は、検査対象が合うことを先に確認し、そのうえで報告までの準備を減らせるかを考えます。
今回確認できたこと
比較には同じOSVスナップショットと、MITを許可する同じライセンスポリシーを使いました。
状態
lodash
HTML出力
脆弱性指摘
コードの確認候補
終了コード
更新前
4.17.20
innerHTML
5
2
1
途中
4.17.21
textContent
3
0
1
更新後
4.18.1
textContent
0
0
0
これはSecuLensの指摘数です。指摘数と実際に悪用可能な問題の数は同じではありません。また、更新後の0件は、使用したデータと検査範囲で一致する指摘がなかったことを表します。
再現用の小さなプロジェクトを用意する
Node.js 22以降と、npm sbom に対応したnpmを使います。今回の環境はNode.js 24.22.1、npm 11.19.1、SecuLens 1.0.0でした。ここからのコマンド例はnpm版です。検証版を指定して導入する場合はnpm install -g seculens@1.0.0を使います。
mkdir seculens-demo
cd seculens-demo
npm init -y
npm install --save-exact lodash@4.17.20 --ignore-scripts
mkdir src
古いバージョンは検査のために意図的に指定しています。検証用フォルダーだけで扱い、アプリとして公開しません。ライブラリを実行する必要もありません。
src/render.js を作ります。これは静的解析に読ませるためのコードで、そのまま実行しません。
export function renderResponse(response, element) {
const output = response.choices[0].message.content;
element.innerHTML = output;
}
export function renderRequest(req, element) {
const input = req.query.message;
element.innerHTML = input;
}
LLMの応答とリクエスト入力をHTMLとして解釈する例です。この検証では文字列を表示できればよいので、後でtextContentに変更します。
部品表と判断基準を用意する
seculens sbom . --format cyclonedx --output sbom-before.json
部品表は、検査対象の名前とバージョンの根拠になります。インストール済み依存関係をもとに作るため、package.jsonを書くだけで終わらず、lockfileと依存関係を準備します。
policy.jsonには、この検証での判断基準を書きます。
{
"allow": ["MIT"],
"deny": ["AGPL-3.0-only"]
}
これは記事用の基準です。自分の製品に適用するときは、利用・配布条件に合わせて決めます。
検査を実行してレポートを開く
seculens scan sbom-before.json --fetch-osv \
--policy policy.json --source ./src \
--customer "記事用検証" --target "依存関係とHTML出力の点検" \
--lang ja --report-style customer \
--output reports/before --fail-on-findings
--fetch-osvはパッケージ名とエコシステムをOSVへ送って候補を取得します。コードやSBOM全体の送信ではありません。社内の依存関係名も持ち出せない環境では、承認済みのデータを用意し、後述の--dbを使います。
今回、最初の情報取得はポリシーなしで実行したため、ライセンス要確認1件を含む8件でした。その後、保存済みDBと上記ポリシーで再検査した7件を、比較表の「更新前」に使っています。条件の異なる集計を混ぜないためです。
今回の比較対象と同じ条件での表示は、次のとおりです。
1 components; 7 findings; 0 incomplete component assessments.
--fail-on-findingsを付けたため、終了コードは1です。コマンドの実行失敗と、指摘があったことによる終了を区別します。
レポートの読み方1 要約で対象と優先順位をつかむ
SecuLensが生成したWordレポートをPDF化して表示。製品UIのモックではなく、今回の実行結果です。
まず表紙で検査対象・検査日時・ツールの版を確認し、次に「検査結果の要約」を開きます。上の画像は、今回出力したWordレポートをPDF化し、プレビューで開いたものです。
今回の表示
読み取れること
次にすること
構成部品1件
今回照合した部品の範囲
想定した依存関係がSBOMに含まれるか確認する
脆弱性5件
バージョンがアドバイザリーの影響範囲に一致した指摘
指摘一覧で対象・参照先を確認する
高2件・中3件
DBの重要度情報に基づく内訳
高の指摘から利用条件・修正情報を読む
コード確認2件
人が確認する必要のある構文が見つかった
該当するファイルと行を読む
脆弱性照合が未判定の構成部品0件
この検査で未判定になった部品はない
対象範囲と警告も確認する
「脆弱性5件」と「コード確認2件」は分けて読みます。 HTML代入の確認候補まで「確定した脆弱性7件」と数えると、読み手に誤解を与えます。
重要度の赤や黄は、確認する順番を決める手がかりです。その環境での悪用可能性や事業影響まで自動で決めた色ではありません。ライセンスの拒否・要確認も、脆弱性の重要度とは別の判断として扱います。
レポートの読み方2 指摘番号から対象を探す
同じWordレポートの3ページ目。番号は後続の詳細に対応しています。
一覧では、番号→重要度・状態→分類→対象の順で読みます。今回のF-001なら、重要度は「高」、分類は「脆弱性」、対象はlodash@4.17.20です。F-006とF-007は「要確認」のコード確認で、render.jsの行番号が記載されています。
ここで対応担当を決められます。依存更新ならそのパッケージの担当者へ、コード確認なら該当ファイルの担当者へ、と切り分けます。報告時にも「上の赤い項目」ではなく「この報告書のF-001」のように参照できます。
レポートの読み方3 詳細で根拠と対応方針を確認する
生成レポートの4ページ目。F-001とF-002の詳細が並びます。
F-001には、lodashの指摘名、CVSS 3基本値7.2、対象バージョン、照合の根拠、推奨対応、関連CVE、参照リンクが並んでいます。
読むときは、次の順にたどります。
対象:自分の使っているパッケージとバージョンかを確認する。
根拠:どのアドバイザリーと影響範囲に一致したのかを見る。
参照先:修正済みバージョンと影響を受ける使い方を確認する。
対応:更新候補を決め、互換性と業務への影響を検証する。
今回のレポートの推奨対応は、参照先の修正情報を読み、更新後の動作を検証するという内容です。環境固有の修正コードを自動で確定したものではありません。直ちに更新できない場合は、理由、代替策、担当者、再確認日をチームの課題管理に残します。
Wordでは概要を読み、すべての参照先や重要度の原情報を追いたい場合は、同じ出力先のreport.jsonを使います。日本語化されるのは主要な見出しと要約で、アドバイザリーの内容などは原文が残ります。
レポートの読み方4 コードとライセンスの要確認を扱う
生成レポートの6ページ目。HTML代入の行が「要確認」として記録されています。
SecuLensのこの検査は構文に基づく候補抽出です。LLMから来た値が関数をまたいでどこまで届くかを証明する検査ではありません。候補の行を読み、今回の用途ではHTMLの解釈が不要なことを確認して、2箇所とも次の形に変更しました。
element.textContent = output;
// リクエスト入力の表示も同様に変更
element.textContent = input;
装飾済みHTMLを表示する製品では、この置き換えだけでは仕様を満たしません。必要な表現と信頼境界を確認し、適切なサニタイズなどを設計します。
ライセンスの欄も同じように、判定とその根拠を分けて読みます。今回のMIT許可ポリシーでは、ライセンスの指摘は0件でした。初回のポリシーなしの検査ではMITが要確認になっています。「MITなのに問題がある」という意味ではなく、チームの判断基準を渡していないためです。
以下は判定の読み方の整理で、今回の画像にすべての状態が出たわけではありません。
ライセンスの状態
読み方
対応
許可
設定したポリシーに合う
利用・配布条件と宣言内容の確認は残る
拒否
設定した拒否条件に当たる
置き換えや利用条件の見直しを検討する
要確認
未指定・不明・判断基準不足など
ライセンス情報とチームの基準を確認する
ライセンス名の照合だけで実際の利用・配布に関する判断を完了するわけではありません。
レポートの読み方5 修正前後を同じ条件で比べる
最初にlodashを4.17.21へ更新しましたが、同じスナップショットで3件の脆弱性指摘が残りました。「以前、修正版とされていたバージョン」への更新だけで判断を終えず、その時点の情報を使って再確認する必要があります。
続いて、検証時のnpmレジストリで確認した4.18.1へ更新しました。これは今回の検証で選んだバージョンで、将来も安全な最新版という意味ではありません。
npm install --save-exact lodash@4.18.1 --ignore-scripts
seculens sbom . --format cyclonedx --output sbom-after.json
seculens scan sbom-after.json \
--db reports/before/database.json \
--policy policy.json --source ./src \
--lang ja --report-style customer \
--output reports/after --fail-on-findings
1 components; 0 findings; 0 incomplete component assessments.
更新後のWordレポートの2ページ目を画像化。今回の範囲で指摘は0件でした。
今回は静的解析用のfixtureなので、製品の動作確認まではしていません。実際の導入では、依存更新後のユニットテストや画面確認も完了条件に加えます。
レポートの読み方6 検査範囲と根拠を確かめて渡す
出力先の4ファイルは、それぞれ役割が違います。
ファイル
誰が何に使うか
report.docx
レビュー担当や報告先が、要約・一覧・詳細を読む
report.json
開発者が指摘の原情報を調べ、自動処理にも使う
sbom.json
どの部品とバージョンを検査したのか確認する
database.json
どの脆弱性情報を使ったか確認し、再検査に使う
更新前の生成レポートの7ページ目。対象部品、照合結果、検査の制約、ハッシュ、警告がまとめられています。
部品表や情報源を探し直さず、報告書と一緒に渡せることも、SecuLensで作業を減らせる点です。SBOMとDBのSHA-256が報告に入るため、同じ入力を使った再確認ができます。報告日時やWordのメタデータまで同一になるという意味ではありません。
今回のDBハッシュは次の値でした。
d2b7f8bded681cb089792d0f428d7dede1c9574781d453f41e3ac6c766b87cbb
また、今回のSBOMにはルートプロジェクトがコンポーネント一覧にないことを示す依存関係の警告が残っています。終了コードや指摘数だけを見ず、警告と検査範囲も読む必要があります。
同じDBを使う検査は変更前後の比較に役立ちます。一方、運用を続けるときはDBの更新も必要です。変更の効果を比べる検査と、新しい脆弱性情報を取り込む検査の両方を用意します。
サプライチェーン攻撃への対処とSecuLensの役割
ライブラリの更新は「既知の脆弱性(CVEなど)」の解消には不可欠ですが、アカウント乗っ取りやタイポスクワッティングなどのソフトウェアサプライチェーン攻撃(xz-utilsやevent-streamなどの事例)に対しては、「最新版に更新した結果、悪意あるコードを取り込んでしまう」リスクがあるため、アップデート単体では防ぎきれません。
1. サプライチェーン攻撃への主な対処法
対策領域
具体的な対策
依存関係の固定と整合性検証
ロックファイル(package-lock.json, poetry.lock など)でバージョンとSHAハッシュ値を厳密に固定し、ビルド時は npm ci 等で決定論的インストールを行う。
SBOM(部品表)とトレーサビリティ
ソフトウェアに含まれる全コンポーネントとそのバージョンをSBOM(CycloneDX / SPDX)として常時記録・追跡できるようにする。
インストール時スクリプトの無効化
パッケージ導入時に npm install --ignore-scripts を指定し、postinstall 等で自動実行されるバックドア・マルウェアを防ぐ。
公開直後パッケージの導入遅延(クールオフ)
新バージョンが公開されて即座に自動マージせず、一定期間(数日〜数週間)空けてコミュニティやセキュリティ監視で悪意あるパッケージとして通報・撤回されないか様子を見る。
環境変数・シークレットの隔離
ビルド環境やCI環境で不要なトークンやクレデンシャルを露出させず、最小権限に絞る(環境変数窃取型マルウェア対策)。
Provenance(出所証明)と署名検証
Sigstore / npm provenance / SLSA などのビルド証明・暗号署名を検証し、公式レポジトリの正規のCIパイプラインでビルドされた成果物か確認する。
動的振る舞い検知・静的解析
パッケージが外部C2サーバーと通信しようとしていないか、難読化コードや eval が仕込まれていないかを静的・動的解析する。
2. SecuLensにおけるサプライチェーン対策
SecuLensは、サプライチェーン防衛において「依存関係の可視化・追跡(SBOM)」「既知の悪意あるパッケージ照合」「ソースコードの危険構文検出」の面で以下のような対策・機能を備えています。
① SBOM(CycloneDX / SPDX)の生成と厳格なトレーサビリティ
SBOM生成時の安全策: SecuLensの seculens sbom コマンドは、インストールスクリプト(postinstall など)を実行せずにSBOMを生成します。
改ざん検知・ハッシュ記録: 生成・入力されたSBOMおよび脆弱性データベースの SHA-256 ハッシュをレポートに記録するため、どの時点のどの成果物かを正確に追跡・監査できます。
② OSVデータベースによる悪意あるパッケージの照合
SecuLensが照合先としている OSV(Open Source Vulnerabilities)には、通常の脆弱性だけでなく GHSA や Malicious Packages(悪意あるパッケージリポジトリ) の情報も集約されています。
報告・撤回されたサプライチェーン攻撃のパッケージが含まれていないかをローカルまたはオンライン(--fetch-osv)で検知可能です。
③ JS/TS ソース解析による難読化・危険コード検出(AST解析)
--source オプションにより、直接の eval や new Function、innerHTML 代入、異常な循環的複雑度・ネスト深度を検出します。
マルウェアがよく用いる「難読化コードの動的評価(eval)」や「不審な動的コード生成」の候補をあぶり出すのに役立ちます。
④ オフライン検査モード(閉域網対応)
事前に取得したデータベース(--db)を用いることで、外部ネットワークへ情報を一切送信せずにローカル環境・エアギャップ環境で安全に検査を実行できます。
3. SecuLensでカバーしきれない領域と推奨される運用
SecuLensは静的スキャンおよび既知情報との照合ツールであるため、以下の脅威には追加の対策を組み合わせる必要があります。
ゼロデイの悪意あるコード(公開直後でOSVに未登録のもの)
対策: パッケージ自動更新ツール(Dependabot / Renovateなど)で「公開から数日〜1週間経過したバージョンのみを対象とする(Cool-off period)」設定を入れる。
npm install 実行時のスクリプト攻撃
対策: CI/CDや開発端末で --ignore-scripts をデフォルト設定にする。
出所証明(Provenance / 署名)の自動検証
対策: 現状のSecuLensはSigstoreやSLSA Provenanceの暗号学的検証までは行わないため、CIパイプライン側で npm audit signatures などを併用する。
SecuLensでSBOM管理・OSV照合・AST危険構文チェックを基盤としつつ、「ロックファイルの厳格運用」「インストールスクリプトの抑制」「自動更新の遅延導入」を組み合わせるのが実効性の高いサプライチェーン防衛になります。
現場に取り入れるなら
まずは1つのプロジェクトで、検査日、lockfile、SBOM、DB、修正内容、再検査結果をひとまとめに保存するところから始められます。「指摘が消えた」だけでなく、対象範囲と残る確認事項まで共有できる状態を目指します。
SecuLensは、その一連の作業のうち、照合と報告の根拠をそろえるための道具です。
参考
SecuLensの紹介・導入手順
SecuLensのソースコードと仕様
Python版の導入とSBOM生成
PHP版の公開パッケージと必要環境
OSV API
npm sbom
CVE-2026-113719: CVE-2026-113719: Arbitrary File Deletion via Path Traversal in virtualenv Seed Package Metadata
CVE-2026-113719: Arbitrary File Deletion via Path Traversal in virtualenv Seed Package Metadata
> Vulnerability ID: CVE-2026-113719
> CVSS Score: 4.5
> Published: 2026-10-23
A path traversal vulnerability in PyPA virtualenv prior to version 21.14.9 allows local attackers to cause arbitrary file and directory deletion when a virtual environment target directory containing untrusted distribution metadata is reused or reseeded.
TL;DR
virtualenv < 21.14.9 fails to validate path boundaries when parsing seed metadata during environment re-creation, allowing local malicious metadata to trigger arbitrary file deletion.
---
⚠️ Exploit Status: POC
Technical Details
- Vulnerability ID: CVE-2026-113719
- CWE ID: CWE-22
- CVSS v3.1 Score: 4.5 (Medium)
- CVSS Vector: CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L
- EPSS Score: 0.00117 (1.57th percentile)
- Impact: Arbitrary File Deletion
- Exploit Status: Proof of Concept / Local Pre-condition
- CISA KEV Status: Not Listed
Affected Systems
- virtualenv versions < 21.14.9 on Python environments (Linux, macOS, Windows)
- virtualenv: < 21.14.9 (Fixed in: 21.14.9)
Code Analysis
Commit: e3cea6f
Limit seed-package cleanup to entries inside the environment.
Limit seed-package cleanup to entries inside the environment. Ignore metadata entries that name the environment itself or resolve outside with a warning.
Exploit Details
- GitHub Security Advisory: Advisory details path traversal vulnerability during seed cleanup.
Mitigation Strategies
- Upgrade virtualenv package to version 21.14.9 or later.
- Enforce explicit --clear flag during automated virtual environment re-creation workflows.
- Restrict directory write permissions on target environments in shared system paths.
Remediation Steps:
1. Run 'pip install --upgrade virtualenv' to update to version >= 21.14.9.
2. Audit automated build scripts and CI/CD pipelines to ensure virtual environment reuse uses the --clear flag.
3. Verify system directory permissions on shared build locations to prevent local untrusted metadata injection.
References
- GitHub Security Advisory GHSA-hhxq-47xx-j5xx
- Fix Commit in pypa/virtualenv
- Pull Request #3428: Limit seed-package cleanup to entries inside the environment
- virtualenv Release Tag 21.14.9
- NVD CVE-2026-113719 Detail
- CVE.org Record for CVE-2026-113719
---
Read the full report for CVE-2026-113719 on our website for more details including interactive diagrams and full exploit analysis.
CVE-2026-113715: CVE-2026-113715: Configuration Key Injection via Unicode Line Boundary in virtualenv
CVE-2026-113715: Configuration Key Injection via Unicode Line Boundary in virtualenv
> Vulnerability ID: CVE-2026-113715
> CVSS Score: 2.0
> Published: 2026-10-23
In virtualenv versions prior to 21.14.9, creating isolated Python virtual environments using the standard library creator (--creator venv) allows configuration key injection into pyvenv.cfg if an attacker can control or influence the destination directory path containing Unicode line break characters.
TL;DR
virtualenv prior to 21.14.9 fails to sanitize Unicode line break characters in destination directory paths, allowing configuration parameter injection into pyvenv.cfg when using the stdlib venv creator.
---
Technical Details
- Vulnerability ID: CVE-2026-113715 / GHSA-qmx5-4p3x-wvqf
- CWE ID: CWE-93 (Improper Neutralization of CRLF Sequences)
- CVSS v4.0 Score: 2.0 (Low)
- CVSS Vector: CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N
- Affected Component: virtualenv (--creator venv)
- Exploit Maturity: None (No public exploit)
- Patched Version: 21.14.9
Affected Systems
- virtualenv (PyPI package prior to 21.14.9)
- virtualenv: < 21.14.9 (Fixed in: 21.14.9)
Code Analysis
Commit: b0008a5
Fix pyvenv.cfg CRLF / line boundary injection via venv creator path
Mitigation Strategies
- Upgrade virtualenv to version 21.14.9 or higher.
- Validate and sanitize untrusted target directory paths prior to environment creation.
- Ensure virtualenv execution does not take unescaped file path inputs directly from unauthenticated users.
Remediation Steps:
1. Run pip install --upgrade 'virtualenv>=21.14.9' in your build environments.
2. Audit automated deployment scripts calling virtualenv --creator venv with user-supplied paths.
3. Verify system installations using virtualenv --version.
References
- GitHub Security Advisory GHSA-qmx5-4p3x-wvqf
- Fix Commit b0008a58885ded9227faf8f910c6c402712c750a
- Pull Request #3426
- virtualenv Release 21.14.9
- NVD Detail CVE-2026-113715
- CVE.org CVE-2026-113715 Record
---
Read the full report for CVE-2026-113715 on our website for more details including interactive diagrams and full exploit analysis.
CVE-2026-113723: CVE-2026-113723: OS Command Injection via Unsafe Prompt Expansion in PyPA virtualenv
CVE-2026-113723: OS Command Injection via Unsafe Prompt Expansion in PyPA virtualenv
> Vulnerability ID: CVE-2026-113723
> CVSS Score: 7.1
> Published: 2026-10-23
A command injection vulnerability in PyPA virtualenv prior to version 21.14.9 allows local attackers to execute arbitrary commands when a user sources a generated shell activation script containing a crafted prompt value.
TL;DR
Unsafe string replacement in virtualenv bash activation scripts allows arbitrary command execution via crafted prompt names during environment activation.
---
⚠️ Exploit Status: POC
Technical Details
- CWE ID: CWE-78
- CVSS v4.0 Score: 7.1 (High)
- Attack Vector: Local
- EPSS Score: 0.00142 (3.23th percentile)
- CISA KEV: Not Listed
- Exploit Status: Proof of Concept Available
- Impact: Arbitrary Code Execution
Affected Systems
- PyPA virtualenv prior to version 21.14.9
- Linux, macOS, and Unix systems sourcing Bash/Zsh activation scripts
- virtualenv: < 21.14.9 (Fixed in: 21.14.9)
Code Analysis
Commit: 5f48d71
Fix PS1 prompt escaping and evaluation logic in bash activation script
Exploit Details
- GitHub Security Advisory: Advisory detailing proof-of-concept command injection via crafted prompt strings.
Mitigation Strategies
- Upgrade virtualenv to version 21.14.9 or higher.
- Regenerate pre-existing virtual environments created with vulnerable virtualenv releases.
- Set VIRTUAL_ENV_DISABLE_PROMPT=1 in shell environments to skip PS1 modifications.
Remediation Steps:
1. Execute 'pip install --upgrade virtualenv>=21.14.9' in the environment build pipeline.
2. Remove old virtual environment directories using 'rm -rf .venv'.
3. Re-create virtual environments using the updated virtualenv package.
4. Verify that bin/activate contains literal variable references for PS1 assignments.
References
- GitHub Security Advisory GHSA-6w3r-xr62-4xcp
- Fix Commit 5f48d711731311428aebfae61d46957762155b22
- GitHub Pull Request #3418
- virtualenv Release 21.14.9
- NIST NVD CVE-2026-113723 Record
- CVE Program Record
---
Read the full report for CVE-2026-113723 on our website for more details including interactive diagrams and full exploit analysis.
Amend PEP440 to limit version identifiers
PEP440 does not contain a limit for version ids. Can we please add one?
things like pypi.org happen without it, and other issues arise not least of which is user a11y related to trying to fit/show all of the wild versioning
stats from pypi:
we have 9.9mm releases, median version is 5 characters, 99th percentil is 20, and maximal is 217(!!)
I propose a 128 char cap, 100 would affect ~9 current releases some releases in the last months with ~110-115 char versioning which I honestly don't understand but... whatever! 128 is fine.
refs:
- warehouse#12483
- warehouse#14965
- packaging.python.org#2163
- packaging-council#11
9 likes
Your Python dependencies installed. Are they vulnerable? 🔐
pip-audit checks environments and requirements for known issues, and fits into CI. Run it before release. 🐍
pypa/pip-audit
#PythonSecurity #浇朋友 #蓝朋友 #互关 #fo
1 like · 68 views
Python 依赖装好了,就一定安全吗?🔐
pip-audit 检查环境和依赖清单中的已知漏洞,还能接入 CI。发版前跑一遍。🐍
pypa/pip-audit
#PythonSecurity #浇朋友 #蓝朋友 #互关 #fo
29 views
@farmersriceCF @samuelcolvin Pip maintainer here. We are part of the bootstrapping chain, you can't pip install pip without a full copy of pip and all it's dependencies.
We use certifi because historically there was no reliable library to read the native trust store, that's changing! pip#14374
3 likes · 86 views
Mentions
12 Green CI Jobs, 422 Security Findings: Lessons from Building an Observability Platform > An engineering journey through Python, React, Docker, GitHub Actions, observability, and software supply chain security. Introduction Does a green CI pipeline mean your application is secure? That question became much more meaningful while developing InfraPulse, a monitoring and observability platform I'm building as a hands-on engineering and technical portfolio project. Throughout development, I implemented automated tests, infrastructure validation, PostgreSQL integration, end-to-end testing, observability checks, and software supply chain security controls. Eventually, the project reached 12 successful GitHub Actions jobs, including dependency integrity checks, secret detection, and Docker image validation. Everything looked promising. Then I introduced dedicated vulnerability scanning. The scanners reported 422 findings, including one classified as critical and 95 as high severity. That doesn't mean InfraPulse has 422 exploitable vulnerabilities. It means the scanners produced 422 findings that still require analysis, correlation, and classification. That distinction matters. In this article, I'll share how the project evolved, the engineering decisions behind its architecture, and what I learned about integrating security into the development lifecycle. --- 1. InfraPulse: From an Application to an Engineering Platform InfraPulse started with a relatively straightforward goal: build an application capable of monitoring services and endpoints, recording results, and exposing operational information. However, I didn't want to build just another functional dashboard. I wanted a project that would allow me to practice engineering approaches commonly used in professional DevOps, SRE, and Platform Engineering environments. That meant answering questions such as: - How can I prevent backend changes from breaking frontend API contracts? - How can I validate real application behavior against PostgreSQL? - How should observability behave when an infrastructure component fails? - How can I verify that Docker images contain the expected artifacts? - How can I prevent dependencies and base images from changing silently? - How can security be introduced without making CI unreliable? These questions directly influenced the architecture. Technology Stack | Layer | Technologies | |---|---| | Backend | Python, FastAPI, SQLAlchemy, Alembic | | Frontend | React, TypeScript, Vite, Mantine | | Database | PostgreSQL 17 | | Containers | Docker, Docker Compose, Nginx | | CI/CD | GitHub Actions | | Testing | Pytest, Vitest, Playwright | | Metrics | Prometheus | | Distributed tracing | OpenTelemetry, Tempo | | Visualization | Grafana | | Alerting | Alertmanager | | Security | Gitleaks, integrity policies, pip-audit, npm audit, Trivy | An important architectural decision was to keep components organized around clear responsibilities. This made it possible to validate individual layers independently while also testing their integrated behavior. 2. The First Challenge: Building Trust in CI Creating a GitHub Actions workflow that runs tests and displays a green checkmark is relatively straightforward. Making sure that green checkmark represents meaningful verification is much harder. InfraPulse's CI pipeline evolved incrementally. I started with backend quality checks and unit tests. Then I expanded coverage to include frontend validation, PostgreSQL integration, API contracts, end-to-end tests, and observability integration. The next step was validating the Docker images produced by the project. Finally, I introduced foundational software supply chain security controls. The 12-Job CI Pipeline In the reference remote execution, all 12 jobs passed: | Job | Responsibility | |---|---| | backend-quality | Backend static quality checks | | backend-unit | Unit tests | | api-contract | OpenAPI contract validation | | frontend-quality | Frontend quality checks | | frontend-tests | Automated frontend tests | | frontend-build | Frontend production build | | backend-postgres | PostgreSQL integration tests | | playwright-e2e | End-to-end tests | | observability-integration | Real observability integration scenarios | | docker-build | Docker builds and image validation | | security-foundations | Foundational security controls | | ci-required | Mandatory aggregate CI gate | The ci-required job plays a particularly important role. It verifies the results of all mandatory validation jobs. This prevents the pipeline from being considered successful when a required job fails, is canceled, or is unexpectedly skipped. A reliable CI gate must validate not only application behavior but also the integrity of the validation process itself. All 12 jobs passed in the reference execution. Figure 1 — GitHub Actions CI Results !Figure 1 — InfraPulse GitHub Actions pipeline showing 12 successful jobs Figure 1. InfraPulse CI pipeline: 12 successful jobs, covering quality, integration, end-to-end testing, observability, Docker image validation, and foundational security controls. Screenshot from a private repository. 3. Testing Observability Means Introducing Failure One of the most interesting lessons was realizing that instrumenting an application is not the same as validating its observability. Adding metrics, logs, and traces is only the beginning. The real challenge is understanding how these signals behave when infrastructure components become unavailable. In InfraPulse, I implemented integration scenarios involving the OpenTelemetry Collector. The tests cover three situations: Normal operation: The application runs while the Collector is available. Outage: The Collector becomes unavailable. Recovery: The Collector returns, and the system's behavior is observed again. These scenarios help validate how the application and its instrumentation behave under changing operational conditions. All three scenarios passed during the reference remote execution. This reinforced an important SRE principle: Observability should be tested under failure conditions, not merely demonstrated through dashboards. Of course, three successful scenarios do not prove complete system resilience. They provide evidence about specific behaviors under controlled conditions. And that is precisely what good engineering validation should do. 4. Docker: A Successful Build Is Not Enough Another important milestone was improving Docker image validation. At first, it might seem sufficient to execute docker build and confirm that the command exits successfully. But that leaves several questions unanswered. Does the image contain the correct files? Does the generated frontend match the expected OpenAPI contract? Does the worker shut down gracefully? Can Nginx serve the expected assets? Are the runtime configurations behaving correctly? To address these questions, I expanded the pipeline with image inspections and smoke tests. OpenAPI Contract and Frontend Consistency The frontend uses TypeScript types generated from the OpenAPI contract. During image validation, the generated types extracted from the frontend build were compared byte-for-byte against an independently generated reference. Both used the contract from the same CI execution and a pinned generator version. The verification did not merely check whether files existed or contained data. It compared their complete contents. This reduces the risk of shipping a frontend built with types that differ from the expected API contract. Runtime Behavior The image validation process also checked: - The API /health endpoint returning HTTP 200. - Readiness returning HTTP 503 when the database was unavailable, as expected in that test scenario. - Worker startup and graceful shutdown using SIGTERM. - Nginx configuration, health checks, and static assets. - Docker image layer inspection. - Cleanup of temporary validation resources. These checks do not replace production testing. However, they provide stronger evidence than a successful image build alone. 5. When DevSecOps Became Part of the Architecture As the pipeline matured, another concern became increasingly important: the integrity of the components used to build and run the software. An application can pass every functional test while still depending on vulnerable packages, mutable container images, or insufficiently controlled build tools. That realization led me to introduce a dedicated software supply chain security phase. The first step focused on foundational controls. Secret Detection I integrated Gitleaks to scan the repository history and working checkout for potentially exposed secrets. Beyond running the scanner, I added synthetic positive and negative test fixtures, operational failure handling, and sanitized output. The goal wasn't simply to execute a security tool. It was to verify that the tool detected known test cases and that operational failures could not silently become successful results. Python Dependency Integrity Initially, the project contained 49 pinned Python requirements without package hashes. Pinning dependency versions improves predictability, but it does not independently verify the integrity of the downloaded artifacts. I introduced hash-verified installations and regression tests that deliberately reject tampered hashes. Build and bootstrap tools were also version-pinned. Docker Images Pinned by Digest Another improvement involved replacing base-image references that relied only on mutable tags with digest-pinned references. A tag can point to different content over time. A digest identifies specific content, helping prevent silent changes to the image base. This improves reproducibility but introduces an additional responsibility: maintaining controlled updates so security fixes can still be incorporated. GitHub Actions Security The workflow security policies also covered: - GitHub Actions pinned to full commit SHAs. - Least-privilege workflow permissions. - Checkout without unnecessary credential persistence. - Automated policies designed to detect regressions in these controls. This work introduced the mandatory security-foundations job. The reference CI execution passed all blocking foundational security policies with zero violations. But one question remained: Were the dependencies and container images free from known vulnerabilities? 6. The Turning Point: 422 Vulnerability Findings After establishing foundational security controls, I started a dedicated vulnerability discovery phase. I used three tools: - pip-audit for Python dependencies. - npm audit for frontend dependencies. - Trivy for Docker image vulnerability scanning. The results were significant. | Target | Findings | |---|---:| | Python — pip-audit 2.10.1 | 2 advisories | | Frontend — npm audit 11.16.0 | 15 occurrences across 14 packages | | Backend image — Trivy 0.76.1 | 176 occurrences | | Nginx image — Trivy | 229 occurrences | | Total | 422 occurrences | Figure 2 — Sanitized Vulnerability Audit Summary !Figure 2 — Sanitized InfraPulse vulnerability audit summary showing 422 findings Figure 2. Sanitized local vulnerability discovery summary. The 422 findings are scanner occurrences, not unique CVEs or confirmed exploitable vulnerabilities. The consolidated severity distribution was: | Reported severity | Occurrences | |---|---:| | Critical | 1 | | High | 95 | | Medium | 143 | | Moderate | 15 | | Low | 159 | | Unknown | 9 | These numbers require careful interpretation. 422 findings do not mean 422 unique CVEs, and they certainly do not prove 422 exploitable vulnerabilities. The same advisory may appear across multiple images, packages, or scanning contexts. Different scanners may also use different severity classifications and remediation information. Furthermore, finding a vulnerable package inside a container image does not automatically demonstrate that the affected functionality is reachable or exploitable through the application. A Concrete Example Trivy flagged CVE-2026-111497 in brotli-libs version 1.2.0-r0, reporting 1.2.1-r0 as the fixed version. The Python audit also reported advisories affecting setuptools, with fixed versions indicated by the scanner. These findings deserve attention, but they still require technical triage. Important questions include: - Is the affected component present in the production runtime or only during the build? - Is a fixed version available and compatible? - Has the operating system distribution already applied a security backport? - Is the vulnerable functionality actually exposed? - Could the proposed update introduce regressions? - Is the same underlying issue being counted across multiple targets? At the time of this analysis, those questions had not yet been fully resolved. Therefore, the vulnerability discovery phase was initially treated as informational, rather than immediately turning every finding into a blocking CI failure. This was a deliberate engineering decision. 7. Why Not Block CI Immediately? It may sound contradictory to identify a critical-severity finding without immediately failing the entire pipeline. But detecting a potential vulnerability and designing a reliable blocking policy are two different activities. A useful security policy must account for evidence quality, applicability, remediation availability, and scanner behavior. Otherwise, CI can become unstable, and teams may start introducing broad exceptions simply to restore a green pipeline. This does not mean ignoring critical findings. It means prioritizing their investigation and defining verifiable remediation criteria. The next stage of InfraPulse will focus on classifying findings, deduplicating occurrences, assessing applicability, and establishing security policies with justified, time-limited exceptions. It is also important to distinguish between different audit outcomes: PASS: The check executed successfully and its acceptance criteria were satisfied. FINDINGS: The audit completed and identified issues requiring evaluation. SCANNER_ERROR: The scanner failed to execute or collect results correctly. NOT_RUN: The audit was not executed. A scanner error must never be interpreted as evidence that no vulnerabilities exist. 8. What I've Learned So Far Building InfraPulse has reinforced several lessons that I consider especially relevant to DevOps and SRE. First: software quality is built in layers. Unit tests, integration tests, E2E tests, image validation, and security checks answer different questions. None completely replaces the others. Second: pipelines should produce evidence. A green status is useful, but engineering confidence also depends on knowing what was executed, which artifacts were tested, and which limitations remain. Third: reproducibility and security are related, but they are not the same thing. Hashes and digests help control the content being used. They do not independently guarantee that the content is vulnerability-free. Fourth: observability must include failure scenarios. A working dashboard under normal conditions is valuable, but outage and recovery tests provide additional evidence about operational behavior. Fifth: automating detection does not automate engineering judgment. Scanners identify potential problems. Determining applicability, priority, and remediation requires context. Perhaps the most important lesson is that security should not be treated as a separate activity performed only at the end of development. It should evolve alongside architecture, testing, infrastructure, and delivery practices. Conclusion InfraPulse began as a monitoring project and evolved into a practical exercise in software engineering, reliability, and security. Reaching 12 successful GitHub Actions jobs was an important milestone. But discovering 422 vulnerability findings demonstrated that a mature pipeline does not mean the security work is finished. Instead, it can provide the foundation needed to identify and investigate additional risks. The next challenge is not simply making the scanners report zero findings. It is building a security policy capable of distinguishing meaningful risks, prioritizing remediation, and preserving a reliable delivery process. In Part 2, I plan to explore vulnerability triage, deduplication, applicability analysis, blocking policies, and auditable exceptions. A green pipeline is evidence of engineering quality. It is not a certificate of zero risk. --- References and Tools - GitHub Actions Documentation - OWASP Software Component Verification Standard - Gitleaks - pip-audit - npm audit - Trivy - CVE-2026-111497 — Official CVE Record - OpenTelemetry - SLSA — Supply-chain Levels for Software Artifacts This article documents an ongoing engineering project. Vulnerability scan results represent a specific point-in-time assessment, not a definitive statement about exploitability or overall application security. Package and remediation versions cited in the case study reflect the local scanner output. InfraPulse is currently maintained in a private repository. The screenshots included in this article are sanitized excerpts of the project's validation results.
Unless your package is huge, your advisory waits --- title: "Unless your package is huge, your advisory waits" published: true description: "It's now taking months for Dependabot and npm audit to flag known vulnerabilities, unless the package is one of the giants." tags: "python, opensource, security, github" canonical_url: "blog.peng.ly" cover_image: "blog.peng.ly" ai_assisted: true --- On 11 July, Weblate published six security advisories on its GitHub repo, each naming the fixed version, 2026.7. Two made it into GitHub's Advisory Database seven weeks later. The other four still haven't, even though GitHub itself published CVEs for them on 26 August. That matters because Dependabot only alerts on advisories GitHub has reviewed into that database. Until then, nobody whose Dependabot watches Weblate hears a thing. GitHub's docs still say review "can take up to 72 hours", and in spring that was about right: Weblate's earlier advisories this year went in within a day or so. Last week I wrote about the same wait for CVEs that reach GitHub through NVD. This time it's advisories maintainers publish themselves, and I wanted to know who's doing the waiting. The big ones get through I picked 250 projects that had an npm or PyPI advisory reviewed by GitHub in 2025, pulled every advisory they've published this year (1,808 of them), and checked each one against GitHub's database on 26 October. Then I sorted them by how often the package gets downloaded in a month. !Advisories reviewed within 30 days of publishing Advisories reviewed within 30 days of publishing (the data) _By the month the maintainer published it, and the package's monthly downloads. Each cell is 11 to 100 advisories from 7 to 21 projects, and September only counts ones published before the 27th, so every one's had 30 days_ Up to March, nearly everything was in the database within a few days, whoever you were. April and May slowed to about a week, still for everyone. Then from June the smaller packages start falling behind, and from July it's a cliff. Packages with over 10 million downloads a month got 93% of their June to August advisories reviewed within 30 days. Under 100k it was 35%, and half of them are still missing now, two to four months on. A few projects publish dozens at once (Flowise did 33 in July), which can swing a month on its own. Counting projects instead tells the same story, though. Of the 17 with August advisories on packages under 100k, one got anything reviewed within 30 days. Not first come, first served October's even starker. Of the advisories published this month for packages over 10 million downloads, 84 of 143 are reviewed already. Half of those are Pillow's batch from last week, all 41 in within a week. For everything smaller, it's 12 of 147, while 55 advisories on smaller packages from before August are still waiting. When I first ran this on 16 October, 301 of the advisories published January to September were waiting. GitHub's reviewed five of them since, two from Angular and one each from Hono, Next.js and devalue. All five are on packages with millions of downloads a month. Criticals don't jump the queue either, even though GitHub's June post on the backlog said "critical issues prioritized". Over the summer, 48% of criticals were reviewed within 30 days, against 72% of lows. Weblate's year Weblate gets about 25,000 downloads a month on PyPI, so it's nowhere near the top. Here's how its advisories have gone this year: - January to May 18 advisories, every one in GitHub's database within a week, most of them the next day. - 1 June One more, reviewed after 37 days. - 11 July Six more. Two reviewed 49 days later, the other four still waiting. - 15 August Five more, none reviewed. - 26 August GitHub, which hands out the CVE IDs for these, published CVEs for seven of the ones still waiting. NVD has them. GitHub's own database doesn't. - 13 October Three more, two of them critical. Nothing yet, though that's only 13 days. The 26 August bit threw me. I'd expected GitHub to publish a CVE when it reviewed the advisory, and it looks like the two aren't tied together any more. Across the 250 projects there are 35 advisories from January to September with a CVE GitHub's published and no entry in its database. Most of Weblate's are in OSV, but only against its git history, not the PyPI package, so osv-scanner on a pip install won't flag them. Mine are waiting too I'm writing this from experience (here's what I've reported). I recently found several quite severe vulnerabilities that are all fixed and public, and still waiting on GitHub's review, so Dependabot hasn't warned anyone about them: - @libredb/studio - any signed-in user can run SQL on a database someone else has open, with that person's credentials (since 21 September) - aedes - one MQTT packet, no login needed, crashes the broker for everyone connected to it (since 15 September) - saxen - nested XML runs it out of memory in a way no try/catch can stop (since 21 September) - comrak - a long list of email addresses in Markdown aborts the whole process when GitHub-style autolinks are on (since 6 September) - social-auth-core - the VK app login lets you sign in as any VK user (since 18 September) - pyhanko-certvalidator - changing the case of a hostname gets a certificate past a name constraint meant to block it (since 10 September) - css_parser - a few dozen bytes of CSS can pin a CPU core for minutes (since 20 September) - strnum - a long run of zeros in a number takes quadratic time, reachable through fast-xml-parser's defaults, on a package with 290 million downloads a month (since 14 August) That said, others I reported made it into GitHub's Advisory Database relatively quickly, like RabbitMQ's Java client in 6 days, simple-git in 10, fast-uri in 11, smol-toml in 13, svgo in 15 and seroval in 16. Stars help, a bit Downloads aren't the whole picture. Some projects with lots of GitHub stars and smaller packages did fine: Open WebUI got all 62 of its summer advisories reviewed within 30 days, and NocoDB all 21. Others didn't. Backstage got 1 of 38 through in time and Directus 2 of 19. Sentry's five are all still waiting, three of them critical. Plenty of these are apps most people run from a Docker image rather than pip or npm, so their downloads undersell how many people actually use them. It isn't that the big packages file tidier advisories. When I checked in mid-October, theirs were no more complete, and complete ones weren't reviewed any faster. GitHub's June post says it prioritises "based on several factors, including project impact and severity", and that it's looking at "additional risk signals for prioritization, such as package usage". From the outside it looks like usage counts for a lot already, but I can only see what comes out of the queue, not how it's run. Doing the big ones first is fair enough, tbh. A bug in axios hits far more people than one in Weblate. But the 72 hours in the docs hasn't been true for months, and for a lot of packages "later" is turning into "not at all". If yours isn't one of them If you maintain something that isn't one of the giants, assume publishing the advisory won't reach anyone's Dependabot for a month or more, maybe not at all. Put it in the release notes in plain words, with the version to upgrade to, and post it wherever your users actually read. If it's on PyPI, you can also send it to PyPA's advisory database as a pull request, which osv-scanner reads. That's not instant either (a few have sat open for weeks), but they merged a couple last week. If you depend on things that aren't huge, a clean Dependabot run means a lot less than it did in spring. For the ones you'd hate to miss, the repo's own Security tab lists every advisory the maintainers have published, reviewed or not, and watching its releases will catch the fix. npm hasn't got a PyPA. There, the release notes are all you've got.
npm・pip・Composerで始めるSecuLens — 日本語レポートを読み、修正と再検査につなげる 脆弱性を検査できても、その結果をチームや顧客に説明する段階で手が止まることがあります。ターミナルの結果を転記し、重要度を並べ替え、部品のバージョンや検査に使った情報を別途添える。検査そのものに加えて、報告の準備にも作業が必要です。 自作のセキュリティ評価CLI SecuLens は、SBOM(ソフトウェア部品表)を読み、既知の脆弱性とライセンスポリシーを点検し、日本語のWordレポートと根拠のJSONをまとめて出力するために作りました。 npm・pip・Composerのうち、使い慣れた環境から導入できます。日本語の対話ウィザードもあるため、初回から長いオプションを覚える必要はありません。この記事では、導入の入口を示したあと、実際のレポートを「どこから読み、何を判断するか」の順で説明します。 今回の実測は、記事用に作ったローカルプロジェクトをTypeScript版で検査した結果です。検証日は2026年10月25日(日本時間)。Python版・PHP版は公開パッケージと公式の手順を確認して紹介しており、この検証で3実装を実行比較したものではありません。 まずは使い慣れたパッケージ管理ツールで導入する 3つすべてを入れる必要はありません。1つ選べば、対応する既存SBOMの脆弱性照合とレポート生成を始められます。 ソース解析も利用する場合は、コードの言語に合った実装を選びます。 実装 必要な環境 追加のソース解析 npm版 Node.js 22以降 JavaScript / TypeScript pip版 Python 3.10以降 Python Composer版 PHP 8.2以降、Composer 2、必要なPHP拡張 PHP Node.jsを使っているならnpm npm install -g seculens seculens wizard --lang ja npmパッケージから導入できます。後半の再現例では、検証時の1.0.0を使用しています。 Pythonを使っているならpip Pythonの仮想環境を有効にした状態で実行します。 python -m pip install seculens seculens wizard --lang ja PyPIパッケージとして配布しています。Python版のSBOM検査・レポート出力のために、Node.jsを別途入れる必要はありません。 PHPを使っているならComposer composer require --dev masahiroid/seculens vendor/bin/seculens wizard --lang ja Packagistパッケージから導入できます。必要なPHP拡張はcurl・dom・mbstring・xml・zipなどです。Composer版の実行コマンドは、プロジェクト内のvendor/bin/seculensになります。 初回は日本語ウィザードで設定する 上の起動コマンドから、既存SBOMの検査かSBOM生成かを選び、入力ファイル、脆弱性情報の取得方法、任意のポリシーやソース解析、出力先などを設定します。顧客名・検査対象名・作成者も指定でき、最後に条件を確認して実行できます。ウィザードの仕様 既存のSBOMがある場合は、それを読み込むところから始められます。SecuLensの導入だけで、どのプロジェクトのSBOMも生成できるわけではありません。 npm版の生成機能はnpm sbomを使い、Python版・PHP版の生成機能は別途Syftを必要とします。CIなどですでに生成したSBOMを使えば、生成ツールを追加する手順を省けます。 他のツールと比べて、どの作業が簡単になるのか 依存関係の既知の脆弱性を調べるだけなら、npmではnpm audit、Pythonではpip-audit、PHPではcomposer auditが直接的な入口です。SecuLensの利点は、検査後に説明できる報告を作り、再確認の材料をそろえる作業をまとめられることにあります。 やりたいこと 既存ツールで始める例 SecuLensでの進め方 使っている依存関係をまず検査する npm audit / pip-audit / composer audit SBOMを用意し、対応する部品をOSVと照合する 既存SBOMを検査する TrivyにもSBOM検査コマンドがある ウィザードで入力と取得方法を選ぶか、scanを実行する 読み手に渡す報告を整える 出力形式を選び、提出先に必要な説明や体裁を整える 日本語の主要見出し・要約、表紙、重要度順一覧、番号付き詳細をWordに出す 検査の根拠を保存する 利用ツールに応じて入力や情報源の保存方法を決める 使用したSBOM・OSVスナップショットと、ハッシュ付き結果を同じ出力先に保存する 各ツールの役割は、npm audit、pip-audit、Composer audit、TrivyのSBOM検査の公式資料を参照しています。これは機能と作業手順の比較で、今回それぞれを同条件で実行して、所要時間や検出精度を測った比較ではありません。 たとえば、すでにSBOMがあれば、npm版では次の1回の検査で報告まで出せます。 seculens scan sbom.json --fetch-osv --lang ja \ --report-style customer --output reports ライセンスポリシーやソース解析を追加しなくても始められます。ライセンスの基準が未指定なら、その部分は要確認になります。慣れたらウィザードで選んだ条件をコマンドにし、同じ手順を繰り返せます。 TrivyにはコンテナーやOSなどを含む広い検査対象があります。SecuLensは、対応するnpm・PyPI・Packagistの部品を照合して報告する用途に絞っています。選定時は、検査対象が合うことを先に確認し、そのうえで報告までの準備を減らせるかを考えます。 今回確認できたこと 比較には同じOSVスナップショットと、MITを許可する同じライセンスポリシーを使いました。 状態 lodash HTML出力 脆弱性指摘 コードの確認候補 終了コード 更新前 4.17.20 innerHTML 5 2 1 途中 4.17.21 textContent 3 0 1 更新後 4.18.1 textContent 0 0 0 これはSecuLensの指摘数です。指摘数と実際に悪用可能な問題の数は同じではありません。また、更新後の0件は、使用したデータと検査範囲で一致する指摘がなかったことを表します。 再現用の小さなプロジェクトを用意する Node.js 22以降と、npm sbom に対応したnpmを使います。今回の環境はNode.js 24.22.1、npm 11.19.1、SecuLens 1.0.0でした。ここからのコマンド例はnpm版です。検証版を指定して導入する場合はnpm install -g seculens@1.0.0を使います。 mkdir seculens-demo cd seculens-demo npm init -y npm install --save-exact lodash@4.17.20 --ignore-scripts mkdir src 古いバージョンは検査のために意図的に指定しています。検証用フォルダーだけで扱い、アプリとして公開しません。ライブラリを実行する必要もありません。 src/render.js を作ります。これは静的解析に読ませるためのコードで、そのまま実行しません。 export function renderResponse(response, element) { const output = response.choices[0].message.content; element.innerHTML = output; } export function renderRequest(req, element) { const input = req.query.message; element.innerHTML = input; } LLMの応答とリクエスト入力をHTMLとして解釈する例です。この検証では文字列を表示できればよいので、後でtextContentに変更します。 部品表と判断基準を用意する seculens sbom . --format cyclonedx --output sbom-before.json 部品表は、検査対象の名前とバージョンの根拠になります。インストール済み依存関係をもとに作るため、package.jsonを書くだけで終わらず、lockfileと依存関係を準備します。 policy.jsonには、この検証での判断基準を書きます。 { "allow": ["MIT"], "deny": ["AGPL-3.0-only"] } これは記事用の基準です。自分の製品に適用するときは、利用・配布条件に合わせて決めます。 検査を実行してレポートを開く seculens scan sbom-before.json --fetch-osv \ --policy policy.json --source ./src \ --customer "記事用検証" --target "依存関係とHTML出力の点検" \ --lang ja --report-style customer \ --output reports/before --fail-on-findings --fetch-osvはパッケージ名とエコシステムをOSVへ送って候補を取得します。コードやSBOM全体の送信ではありません。社内の依存関係名も持ち出せない環境では、承認済みのデータを用意し、後述の--dbを使います。 今回、最初の情報取得はポリシーなしで実行したため、ライセンス要確認1件を含む8件でした。その後、保存済みDBと上記ポリシーで再検査した7件を、比較表の「更新前」に使っています。条件の異なる集計を混ぜないためです。 今回の比較対象と同じ条件での表示は、次のとおりです。 1 components; 7 findings; 0 incomplete component assessments. --fail-on-findingsを付けたため、終了コードは1です。コマンドの実行失敗と、指摘があったことによる終了を区別します。 レポートの読み方1 要約で対象と優先順位をつかむ SecuLensが生成したWordレポートをPDF化して表示。製品UIのモックではなく、今回の実行結果です。 まず表紙で検査対象・検査日時・ツールの版を確認し、次に「検査結果の要約」を開きます。上の画像は、今回出力したWordレポートをPDF化し、プレビューで開いたものです。 今回の表示 読み取れること 次にすること 構成部品1件 今回照合した部品の範囲 想定した依存関係がSBOMに含まれるか確認する 脆弱性5件 バージョンがアドバイザリーの影響範囲に一致した指摘 指摘一覧で対象・参照先を確認する 高2件・中3件 DBの重要度情報に基づく内訳 高の指摘から利用条件・修正情報を読む コード確認2件 人が確認する必要のある構文が見つかった 該当するファイルと行を読む 脆弱性照合が未判定の構成部品0件 この検査で未判定になった部品はない 対象範囲と警告も確認する 「脆弱性5件」と「コード確認2件」は分けて読みます。 HTML代入の確認候補まで「確定した脆弱性7件」と数えると、読み手に誤解を与えます。 重要度の赤や黄は、確認する順番を決める手がかりです。その環境での悪用可能性や事業影響まで自動で決めた色ではありません。ライセンスの拒否・要確認も、脆弱性の重要度とは別の判断として扱います。 レポートの読み方2 指摘番号から対象を探す 同じWordレポートの3ページ目。番号は後続の詳細に対応しています。 一覧では、番号→重要度・状態→分類→対象の順で読みます。今回のF-001なら、重要度は「高」、分類は「脆弱性」、対象はlodash@4.17.20です。F-006とF-007は「要確認」のコード確認で、render.jsの行番号が記載されています。 ここで対応担当を決められます。依存更新ならそのパッケージの担当者へ、コード確認なら該当ファイルの担当者へ、と切り分けます。報告時にも「上の赤い項目」ではなく「この報告書のF-001」のように参照できます。 レポートの読み方3 詳細で根拠と対応方針を確認する 生成レポートの4ページ目。F-001とF-002の詳細が並びます。 F-001には、lodashの指摘名、CVSS 3基本値7.2、対象バージョン、照合の根拠、推奨対応、関連CVE、参照リンクが並んでいます。 読むときは、次の順にたどります。 対象:自分の使っているパッケージとバージョンかを確認する。 根拠:どのアドバイザリーと影響範囲に一致したのかを見る。 参照先:修正済みバージョンと影響を受ける使い方を確認する。 対応:更新候補を決め、互換性と業務への影響を検証する。 今回のレポートの推奨対応は、参照先の修正情報を読み、更新後の動作を検証するという内容です。環境固有の修正コードを自動で確定したものではありません。直ちに更新できない場合は、理由、代替策、担当者、再確認日をチームの課題管理に残します。 Wordでは概要を読み、すべての参照先や重要度の原情報を追いたい場合は、同じ出力先のreport.jsonを使います。日本語化されるのは主要な見出しと要約で、アドバイザリーの内容などは原文が残ります。 レポートの読み方4 コードとライセンスの要確認を扱う 生成レポートの6ページ目。HTML代入の行が「要確認」として記録されています。 SecuLensのこの検査は構文に基づく候補抽出です。LLMから来た値が関数をまたいでどこまで届くかを証明する検査ではありません。候補の行を読み、今回の用途ではHTMLの解釈が不要なことを確認して、2箇所とも次の形に変更しました。 element.textContent = output; // リクエスト入力の表示も同様に変更 element.textContent = input; 装飾済みHTMLを表示する製品では、この置き換えだけでは仕様を満たしません。必要な表現と信頼境界を確認し、適切なサニタイズなどを設計します。 ライセンスの欄も同じように、判定とその根拠を分けて読みます。今回のMIT許可ポリシーでは、ライセンスの指摘は0件でした。初回のポリシーなしの検査ではMITが要確認になっています。「MITなのに問題がある」という意味ではなく、チームの判断基準を渡していないためです。 以下は判定の読み方の整理で、今回の画像にすべての状態が出たわけではありません。 ライセンスの状態 読み方 対応 許可 設定したポリシーに合う 利用・配布条件と宣言内容の確認は残る 拒否 設定した拒否条件に当たる 置き換えや利用条件の見直しを検討する 要確認 未指定・不明・判断基準不足など ライセンス情報とチームの基準を確認する ライセンス名の照合だけで実際の利用・配布に関する判断を完了するわけではありません。 レポートの読み方5 修正前後を同じ条件で比べる 最初にlodashを4.17.21へ更新しましたが、同じスナップショットで3件の脆弱性指摘が残りました。「以前、修正版とされていたバージョン」への更新だけで判断を終えず、その時点の情報を使って再確認する必要があります。 続いて、検証時のnpmレジストリで確認した4.18.1へ更新しました。これは今回の検証で選んだバージョンで、将来も安全な最新版という意味ではありません。 npm install --save-exact lodash@4.18.1 --ignore-scripts seculens sbom . --format cyclonedx --output sbom-after.json seculens scan sbom-after.json \ --db reports/before/database.json \ --policy policy.json --source ./src \ --lang ja --report-style customer \ --output reports/after --fail-on-findings 1 components; 0 findings; 0 incomplete component assessments. 更新後のWordレポートの2ページ目を画像化。今回の範囲で指摘は0件でした。 今回は静的解析用のfixtureなので、製品の動作確認まではしていません。実際の導入では、依存更新後のユニットテストや画面確認も完了条件に加えます。 レポートの読み方6 検査範囲と根拠を確かめて渡す 出力先の4ファイルは、それぞれ役割が違います。 ファイル 誰が何に使うか report.docx レビュー担当や報告先が、要約・一覧・詳細を読む report.json 開発者が指摘の原情報を調べ、自動処理にも使う sbom.json どの部品とバージョンを検査したのか確認する database.json どの脆弱性情報を使ったか確認し、再検査に使う 更新前の生成レポートの7ページ目。対象部品、照合結果、検査の制約、ハッシュ、警告がまとめられています。 部品表や情報源を探し直さず、報告書と一緒に渡せることも、SecuLensで作業を減らせる点です。SBOMとDBのSHA-256が報告に入るため、同じ入力を使った再確認ができます。報告日時やWordのメタデータまで同一になるという意味ではありません。 今回のDBハッシュは次の値でした。 d2b7f8bded681cb089792d0f428d7dede1c9574781d453f41e3ac6c766b87cbb また、今回のSBOMにはルートプロジェクトがコンポーネント一覧にないことを示す依存関係の警告が残っています。終了コードや指摘数だけを見ず、警告と検査範囲も読む必要があります。 同じDBを使う検査は変更前後の比較に役立ちます。一方、運用を続けるときはDBの更新も必要です。変更の効果を比べる検査と、新しい脆弱性情報を取り込む検査の両方を用意します。 サプライチェーン攻撃への対処とSecuLensの役割 ライブラリの更新は「既知の脆弱性(CVEなど)」の解消には不可欠ですが、アカウント乗っ取りやタイポスクワッティングなどのソフトウェアサプライチェーン攻撃(xz-utilsやevent-streamなどの事例)に対しては、「最新版に更新した結果、悪意あるコードを取り込んでしまう」リスクがあるため、アップデート単体では防ぎきれません。 1. サプライチェーン攻撃への主な対処法 対策領域 具体的な対策 依存関係の固定と整合性検証 ロックファイル(package-lock.json, poetry.lock など)でバージョンとSHAハッシュ値を厳密に固定し、ビルド時は npm ci 等で決定論的インストールを行う。 SBOM(部品表)とトレーサビリティ ソフトウェアに含まれる全コンポーネントとそのバージョンをSBOM(CycloneDX / SPDX)として常時記録・追跡できるようにする。 インストール時スクリプトの無効化 パッケージ導入時に npm install --ignore-scripts を指定し、postinstall 等で自動実行されるバックドア・マルウェアを防ぐ。 公開直後パッケージの導入遅延(クールオフ) 新バージョンが公開されて即座に自動マージせず、一定期間(数日〜数週間)空けてコミュニティやセキュリティ監視で悪意あるパッケージとして通報・撤回されないか様子を見る。 環境変数・シークレットの隔離 ビルド環境やCI環境で不要なトークンやクレデンシャルを露出させず、最小権限に絞る(環境変数窃取型マルウェア対策)。 Provenance(出所証明)と署名検証 Sigstore / npm provenance / SLSA などのビルド証明・暗号署名を検証し、公式レポジトリの正規のCIパイプラインでビルドされた成果物か確認する。 動的振る舞い検知・静的解析 パッケージが外部C2サーバーと通信しようとしていないか、難読化コードや eval が仕込まれていないかを静的・動的解析する。 2. SecuLensにおけるサプライチェーン対策 SecuLensは、サプライチェーン防衛において「依存関係の可視化・追跡(SBOM)」「既知の悪意あるパッケージ照合」「ソースコードの危険構文検出」の面で以下のような対策・機能を備えています。 ① SBOM(CycloneDX / SPDX)の生成と厳格なトレーサビリティ SBOM生成時の安全策: SecuLensの seculens sbom コマンドは、インストールスクリプト(postinstall など)を実行せずにSBOMを生成します。 改ざん検知・ハッシュ記録: 生成・入力されたSBOMおよび脆弱性データベースの SHA-256 ハッシュをレポートに記録するため、どの時点のどの成果物かを正確に追跡・監査できます。 ② OSVデータベースによる悪意あるパッケージの照合 SecuLensが照合先としている OSV(Open Source Vulnerabilities)には、通常の脆弱性だけでなく GHSA や Malicious Packages(悪意あるパッケージリポジトリ) の情報も集約されています。 報告・撤回されたサプライチェーン攻撃のパッケージが含まれていないかをローカルまたはオンライン(--fetch-osv)で検知可能です。 ③ JS/TS ソース解析による難読化・危険コード検出(AST解析) --source オプションにより、直接の eval や new Function、innerHTML 代入、異常な循環的複雑度・ネスト深度を検出します。 マルウェアがよく用いる「難読化コードの動的評価(eval)」や「不審な動的コード生成」の候補をあぶり出すのに役立ちます。 ④ オフライン検査モード(閉域網対応) 事前に取得したデータベース(--db)を用いることで、外部ネットワークへ情報を一切送信せずにローカル環境・エアギャップ環境で安全に検査を実行できます。 3. SecuLensでカバーしきれない領域と推奨される運用 SecuLensは静的スキャンおよび既知情報との照合ツールであるため、以下の脅威には追加の対策を組み合わせる必要があります。 ゼロデイの悪意あるコード(公開直後でOSVに未登録のもの) 対策: パッケージ自動更新ツール(Dependabot / Renovateなど)で「公開から数日〜1週間経過したバージョンのみを対象とする(Cool-off period)」設定を入れる。 npm install 実行時のスクリプト攻撃 対策: CI/CDや開発端末で --ignore-scripts をデフォルト設定にする。 出所証明(Provenance / 署名)の自動検証 対策: 現状のSecuLensはSigstoreやSLSA Provenanceの暗号学的検証までは行わないため、CIパイプライン側で npm audit signatures などを併用する。 SecuLensでSBOM管理・OSV照合・AST危険構文チェックを基盤としつつ、「ロックファイルの厳格運用」「インストールスクリプトの抑制」「自動更新の遅延導入」を組み合わせるのが実効性の高いサプライチェーン防衛になります。 現場に取り入れるなら まずは1つのプロジェクトで、検査日、lockfile、SBOM、DB、修正内容、再検査結果をひとまとめに保存するところから始められます。「指摘が消えた」だけでなく、対象範囲と残る確認事項まで共有できる状態を目指します。 SecuLensは、その一連の作業のうち、照合と報告の根拠をそろえるための道具です。 参考 SecuLensの紹介・導入手順 SecuLensのソースコードと仕様 Python版の導入とSBOM生成 PHP版の公開パッケージと必要環境 OSV API npm sbom
CVE-2026-113719: CVE-2026-113719: Arbitrary File Deletion via Path Traversal in virtualenv Seed Package Metadata CVE-2026-113719: Arbitrary File Deletion via Path Traversal in virtualenv Seed Package Metadata > Vulnerability ID: CVE-2026-113719 > CVSS Score: 4.5 > Published: 2026-10-23 A path traversal vulnerability in PyPA virtualenv prior to version 21.14.9 allows local attackers to cause arbitrary file and directory deletion when a virtual environment target directory containing untrusted distribution metadata is reused or reseeded. TL;DR virtualenv < 21.14.9 fails to validate path boundaries when parsing seed metadata during environment re-creation, allowing local malicious metadata to trigger arbitrary file deletion. --- ⚠️ Exploit Status: POC Technical Details - Vulnerability ID: CVE-2026-113719 - CWE ID: CWE-22 - CVSS v3.1 Score: 4.5 (Medium) - CVSS Vector: CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L - EPSS Score: 0.00117 (1.57th percentile) - Impact: Arbitrary File Deletion - Exploit Status: Proof of Concept / Local Pre-condition - CISA KEV Status: Not Listed Affected Systems - virtualenv versions < 21.14.9 on Python environments (Linux, macOS, Windows) - virtualenv: < 21.14.9 (Fixed in: 21.14.9) Code Analysis Commit: e3cea6f Limit seed-package cleanup to entries inside the environment. Limit seed-package cleanup to entries inside the environment. Ignore metadata entries that name the environment itself or resolve outside with a warning. Exploit Details - GitHub Security Advisory: Advisory details path traversal vulnerability during seed cleanup. Mitigation Strategies - Upgrade virtualenv package to version 21.14.9 or later. - Enforce explicit --clear flag during automated virtual environment re-creation workflows. - Restrict directory write permissions on target environments in shared system paths. Remediation Steps: 1. Run 'pip install --upgrade virtualenv' to update to version >= 21.14.9. 2. Audit automated build scripts and CI/CD pipelines to ensure virtual environment reuse uses the --clear flag. 3. Verify system directory permissions on shared build locations to prevent local untrusted metadata injection. References - GitHub Security Advisory GHSA-hhxq-47xx-j5xx - Fix Commit in pypa/virtualenv - Pull Request #3428: Limit seed-package cleanup to entries inside the environment - virtualenv Release Tag 21.14.9 - NVD CVE-2026-113719 Detail - CVE.org Record for CVE-2026-113719 --- Read the full report for CVE-2026-113719 on our website for more details including interactive diagrams and full exploit analysis.
CVE-2026-113715: CVE-2026-113715: Configuration Key Injection via Unicode Line Boundary in virtualenv CVE-2026-113715: Configuration Key Injection via Unicode Line Boundary in virtualenv > Vulnerability ID: CVE-2026-113715 > CVSS Score: 2.0 > Published: 2026-10-23 In virtualenv versions prior to 21.14.9, creating isolated Python virtual environments using the standard library creator (--creator venv) allows configuration key injection into pyvenv.cfg if an attacker can control or influence the destination directory path containing Unicode line break characters. TL;DR virtualenv prior to 21.14.9 fails to sanitize Unicode line break characters in destination directory paths, allowing configuration parameter injection into pyvenv.cfg when using the stdlib venv creator. --- Technical Details - Vulnerability ID: CVE-2026-113715 / GHSA-qmx5-4p3x-wvqf - CWE ID: CWE-93 (Improper Neutralization of CRLF Sequences) - CVSS v4.0 Score: 2.0 (Low) - CVSS Vector: CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N - Affected Component: virtualenv (--creator venv) - Exploit Maturity: None (No public exploit) - Patched Version: 21.14.9 Affected Systems - virtualenv (PyPI package prior to 21.14.9) - virtualenv: < 21.14.9 (Fixed in: 21.14.9) Code Analysis Commit: b0008a5 Fix pyvenv.cfg CRLF / line boundary injection via venv creator path Mitigation Strategies - Upgrade virtualenv to version 21.14.9 or higher. - Validate and sanitize untrusted target directory paths prior to environment creation. - Ensure virtualenv execution does not take unescaped file path inputs directly from unauthenticated users. Remediation Steps: 1. Run pip install --upgrade 'virtualenv>=21.14.9' in your build environments. 2. Audit automated deployment scripts calling virtualenv --creator venv with user-supplied paths. 3. Verify system installations using virtualenv --version. References - GitHub Security Advisory GHSA-qmx5-4p3x-wvqf - Fix Commit b0008a58885ded9227faf8f910c6c402712c750a - Pull Request #3426 - virtualenv Release 21.14.9 - NVD Detail CVE-2026-113715 - CVE.org CVE-2026-113715 Record --- Read the full report for CVE-2026-113715 on our website for more details including interactive diagrams and full exploit analysis.
CVE-2026-113723: CVE-2026-113723: OS Command Injection via Unsafe Prompt Expansion in PyPA virtualenv CVE-2026-113723: OS Command Injection via Unsafe Prompt Expansion in PyPA virtualenv > Vulnerability ID: CVE-2026-113723 > CVSS Score: 7.1 > Published: 2026-10-23 A command injection vulnerability in PyPA virtualenv prior to version 21.14.9 allows local attackers to execute arbitrary commands when a user sources a generated shell activation script containing a crafted prompt value. TL;DR Unsafe string replacement in virtualenv bash activation scripts allows arbitrary command execution via crafted prompt names during environment activation. --- ⚠️ Exploit Status: POC Technical Details - CWE ID: CWE-78 - CVSS v4.0 Score: 7.1 (High) - Attack Vector: Local - EPSS Score: 0.00142 (3.23th percentile) - CISA KEV: Not Listed - Exploit Status: Proof of Concept Available - Impact: Arbitrary Code Execution Affected Systems - PyPA virtualenv prior to version 21.14.9 - Linux, macOS, and Unix systems sourcing Bash/Zsh activation scripts - virtualenv: < 21.14.9 (Fixed in: 21.14.9) Code Analysis Commit: 5f48d71 Fix PS1 prompt escaping and evaluation logic in bash activation script Exploit Details - GitHub Security Advisory: Advisory detailing proof-of-concept command injection via crafted prompt strings. Mitigation Strategies - Upgrade virtualenv to version 21.14.9 or higher. - Regenerate pre-existing virtual environments created with vulnerable virtualenv releases. - Set VIRTUAL_ENV_DISABLE_PROMPT=1 in shell environments to skip PS1 modifications. Remediation Steps: 1. Execute 'pip install --upgrade virtualenv>=21.14.9' in the environment build pipeline. 2. Remove old virtual environment directories using 'rm -rf .venv'. 3. Re-create virtual environments using the updated virtualenv package. 4. Verify that bin/activate contains literal variable references for PS1 assignments. References - GitHub Security Advisory GHSA-6w3r-xr62-4xcp - Fix Commit 5f48d711731311428aebfae61d46957762155b22 - GitHub Pull Request #3418 - virtualenv Release 21.14.9 - NIST NVD CVE-2026-113723 Record - CVE Program Record --- Read the full report for CVE-2026-113723 on our website for more details including interactive diagrams and full exploit analysis.
Amend PEP440 to limit version identifiers PEP440 does not contain a limit for version ids. Can we please add one? things like pypi.org happen without it, and other issues arise not least of which is user a11y related to trying to fit/show all of the wild versioning stats from pypi: we have 9.9mm releases, median version is 5 characters, 99th percentil is 20, and maximal is 217(!!) I propose a 128 char cap, 100 would affect ~9 current releases some releases in the last months with ~110-115 char versioning which I honestly don't understand but... whatever! 128 is fine. refs: - warehouse#12483 - warehouse#14965 - packaging.python.org#2163 - packaging-council#11
Your Python dependencies installed. Are they vulnerable? 🔐 pip-audit checks environments and requirements for known issues, and fits into CI. Run it before release. 🐍 pypa/pip-audit #PythonSecurity #浇朋友 #蓝朋友 #互关 #fo
Python 依赖装好了,就一定安全吗?🔐 pip-audit 检查环境和依赖清单中的已知漏洞,还能接入 CI。发版前跑一遍。🐍 pypa/pip-audit #PythonSecurity #浇朋友 #蓝朋友 #互关 #fo
@farmersriceCF @samuelcolvin Pip maintainer here. We are part of the bootstrapping chain, you can't pip install pip without a full copy of pip and all it's dependencies. We use certifi because historically there was no reliable library to read the native trust store, that's changing! pip#14374