Skip to content

feat(pyproject): add workspace/monorepo support#2

Closed
Strum355 wants to merge 103 commits into
mainfrom
TC-4039
Closed

feat(pyproject): add workspace/monorepo support#2
Strum355 wants to merge 103 commits into
mainfrom
TC-4039

Conversation

@Strum355

@Strum355 Strum355 commented Apr 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add lock file walk-up (_findLockFileDir) and workspace root detection (_isWorkspaceRoot) to Base_pyproject, following the Base_javascript pattern
  • Handle uv editable installs (-e) in _parseUvExport so workspace members appear in the dependency graph with correct multi-level linkage
  • Support TRUSTIFY_DA_WORKSPACE_DIR override for directing lock file search
  • Run tool commands (uv export, poetry show) from the workspace root where the lock file lives

Implements TC-4039

Test plan

  • validateLockFile() finds lock files in parent directories (uv and poetry)
  • validateLockFile() stops at workspace root boundary when lock file is absent
  • TRUSTIFY_DA_WORKSPACE_DIR override directs lock file search
  • SBOM generation for uv workspace root, mid-package, and sub-package (3-level nesting)
  • SBOM generation for poetry workspace root and sub-package
  • Existing non-workspace pyproject.toml tests continue to pass

🤖 Generated with Claude Code

Strum355 and others added 30 commits June 20, 2025 15:11
Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
* feat!: set golang mvs true as default

Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>

* feat: update tests

Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>

* feat: update provider to trustify

Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>

* fix: ci build

Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>

* fix: update new perl-base sbom

Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>

* fix: align providers

Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>

---------

Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
* chore: rename trustification references to guacsec

* chore: remove building OCI image in stage pipeline

* chore: rename EXHORT_ opts to TRUSTIFY_DA_

* chore: move test url to env var
Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
Strum355 and others added 29 commits February 3, 2026 12:08
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](softprops/action-gh-release@v1...v2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '2'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…updates (guacsec#401)

Bumps the github-actions group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `7` | `8` |
| [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `3` | `4` |
| [docker/login-action](https://github.com/docker/login-action) | `3` | `4` |
| [docker/metadata-action](https://github.com/docker/metadata-action) | `5` | `6` |
| [docker/build-push-action](https://github.com/docker/build-push-action) | `6` | `7` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `6` | `7` |



Updates `actions/download-artifact` from 7 to 8
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@v7...v8)

Updates `docker/setup-buildx-action` from 3 to 4
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](docker/setup-buildx-action@v3...v4)

Updates `docker/login-action` from 3 to 4
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](docker/login-action@v3...v4)

Updates `docker/metadata-action` from 5 to 6
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](docker/metadata-action@v5...v6)

Updates `docker/build-push-action` from 6 to 7
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](docker/build-push-action@v6...v7)

Updates `actions/upload-artifact` from 6 to 7
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@v6...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: docker/login-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: docker/metadata-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: docker/build-push-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [minimatch](https://github.com/isaacs/minimatch) and [editorconfig](https://github.com/editorconfig/editorconfig-core-js). These dependencies needed to be updated together.

Updates `minimatch` from 9.0.5 to 9.0.9
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](isaacs/minimatch@v9.0.5...v9.0.9)

Updates `minimatch` from 3.1.2 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](isaacs/minimatch@v9.0.5...v9.0.9)

Updates `minimatch` from 5.1.6 to 5.1.9
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](isaacs/minimatch@v9.0.5...v9.0.9)

Updates `editorconfig` from 1.0.4 to 1.0.7
- [Release notes](https://github.com/editorconfig/editorconfig-core-js/releases)
- [Changelog](https://github.com/editorconfig/editorconfig-core-js/blob/main/CHANGELOG.md)
- [Commits](editorconfig/editorconfig-core-js@v1.0.4...v1.0.7)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 9.0.9
  dependency-type: indirect
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
- dependency-name: minimatch
  dependency-version: 5.1.9
  dependency-type: indirect
- dependency-name: editorconfig
  dependency-version: 1.0.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat: implement license resolution and identification
* feat: calculate compatibility from backend information

---------

Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
Co-authored-by: Claude Sonnet <noreply@anthropic.com>
…nse response to SBOM purls (guacsec#414)

* fix: use PackageURL to canonicalize purls when matching deps.dev license response to SBOM purls

The deps.dev backend returns package purls with qualifiers like ?scope=compile
(e.g. pkg:maven/org.mariadb.jdbc/mariadb-java-client@3.1.4?scope=compile),
while the SBOM generated by the client uses plain purls without qualifiers.
The strict purls.includes(purl) check in normalizeLicensesResponse never
matched qualifier-suffixed purls, causing incompatibleDependencies to always
be empty even when LGPL/copyleft dependencies were present.

Fix by using PackageURL.fromString() to parse purls and reconstructing them
without qualifiers or subpath via new PackageURL(..., null, null). This
produces canonical core purls for both the allowed set and the backend keys,
making the match resilient to any qualifiers the backend may return.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove unused getLicenseForSbom function

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rust analysis support

* fix: use a proper toml parser

* fix: remove duplicate functions

* fix: update after review

* fix: revert accidental changes to package-lock.json

* fix: broken sboms

* feat: include path dependencies in SBOM with repository_url=local qualifier

---------

Signed-off-by: Adva Oren <aoren@redhat.com>
…csec#422)

fix: analysis of an empty go manifest returns non-zero scanned depenedencies
* feat: allow monorepo lock files using workspaceDir

Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Implements TC-3862

* refactor: improve workspace discovery robustness and batch analysis maintainability

- Replace hand-rolled pnpm-workspace.yaml parser with js-yaml
- Fix negation pattern handling in workspace discovery (e.g. !**/test/**)
- Refactor stackAnalysisBatch into focused helpers, eliminating duplicated
  SBOM generation logic between fail-fast and continue-on-error paths
- Add integration tests for stackAnalysisBatch with mocked providers and
  HTTP backend

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements TC-3862

* fix: address qodo review findings for batch analysis

- Make generateOneSbom async and await provider.provideStack() to support
  async providers (e.g. python_pip)
- Propagate workspaceDir as cwd for package manager commands so npm/pnpm/yarn
  run from workspace root in monorepos
- Fix CLI --html --metadata printing wrapper object instead of HTML string

Implements TC-3862

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: use TRUSTIFY_DA_WORKSPACE_DIR convention consistently

Remove the opts.workspaceDir fallback pattern and use only the
TRUSTIFY_DA_WORKSPACE_DIR key through getCustom(), keeping the
existing single-convention pattern for option propagation.

TC-3862

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve eslint warnings in workspace.js and batch test

Use named import for js-yaml load function and fix import ordering
in stack_analysis_batch.test.js.

TC-3862

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
…c#431)

* feat: walk up directory tree to find JS lockfile in monorepos

Add parent-traversal logic to Base_javascript.validateLockFile and
_buildDependencyTree, matching the existing Cargo provider pattern.
When a nested package.json has no lockfile in its directory, the
provider now walks up looking for package-lock.json/yarn.lock/
pnpm-lock.yaml, stopping at a package.json with "workspaces" field
(the workspace root boundary) or the filesystem root.

This enables single-file stack analysis to work for modules in
JS/TS monorepos where the lockfile lives at the workspace root.

TRUSTIFY_DA_WORKSPACE_DIR still takes precedence as an explicit
override (no walk-up when set).

Assisted-by: Claude Code

Ref: TC-3891

* fix: pass correct cwd to commands and trim unnecessary comments

Address Qodo review: #executeListCmd and #createLockFile now pass
{ cwd: manifestDir } to #invokeCommand so commands run from the
lockfile directory, not the manifest directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Ref: TC-3891

* fix: add pnpm-workspace.yaml as workspace root boundary

Address Qodo review: _findLockFileDir now also stops at directories
containing pnpm-workspace.yaml, preventing the walk-up from escaping
pnpm workspace roots that don't use package.json#workspaces.

Ref: TC-3891

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…ec#444)

p-limit v5 uses Node.js subpath imports (#async_hooks) which webpack
cannot resolve, breaking downstream consumers that bundle the client
(e.g., the VS Code extension). v4 has an identical API without the
subpath imports.

Fixes guacsec#443

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…nalysis

pnpm, yarn classic, and yarn berry providers were missing peer and/or
optional dependencies from the SBOM, while yarn berry additionally
included devDependencies. This caused inconsistent vulnerability reports
across package managers for the same package.json.

Root causes:
- Manifest.loadDependencies() only read content.dependencies
- base_javascript _getRootDependencies() and _addDependenciesToSbom()
  ignored pnpm's separate optionalDependencies key
- yarn berry had no mechanism to filter out devDependencies from
  yarn info --all output

Fixes:
- Manifest now loads peer, optional, and bundled dependencies
- Base JS provider merges optionalDependencies from dep tree output
- ensurePeerAndOptionalDeps() injects manifest-declared peer/optional
  deps when the package manager does not resolve them
- Yarn berry processor filters root dependencies against manifest

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
…ct purl matching

Address two review comments:

1. Yarn berry addDependenciesToSbom() now builds a reachable set via
   BFS from root production deps, so devDependency nodes and their
   entire transitive trees are excluded from the SBOM — not just the
   root edges.

2. Replace substring-based checkIfPackageInsideDependsOnList() with
   purl-prefix matching in ensurePeerAndOptionalDeps() to avoid false
   positives (e.g. "minimist" matching "minimist-options") and false
   negatives (e.g. "@hapi/joi" not matching URL-encoded "%40hapi/joi").

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
fix: include peer, optional, and bundled deps in JS stack/component a…
@Strum355 Strum355 closed this Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants