From d6cc46443826ec5237bfbb36f05374df8c291aab Mon Sep 17 00:00:00 2001 From: Ruben Romero Montes Date: Wed, 29 Apr 2026 13:12:34 +0200 Subject: [PATCH 1/2] fix: add uv to root-level ignore and use explicit paths Add root-level `ignore: [{dependency-name: "*"}]` for the uv ecosystem, missed in PR #431. Remove redundant per-directory entries for ecosystems covered by root-level ignore-all. Replace glob patterns with explicit directory listings for maven test fixtures, since `/**` globs don't reliably match nested subdirectories for security updates. Add test fixture guidance to CONVENTIONS.md. Co-Authored-By: Claude Opus 4.6 --- .github/dependabot.yml | 87 ++++++++++++----------------------- CONVENTIONS.md | 100 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 58 deletions(-) create mode 100644 CONVENTIONS.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c21cade5..af028a69 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -34,7 +34,7 @@ updates: labels: [] ignore: - dependency-name: "*" - - package-ecosystem: "gomod" + - package-ecosystem: "uv" directory: "/" schedule: interval: "monthly" @@ -42,7 +42,7 @@ updates: labels: [] ignore: - dependency-name: "*" - - package-ecosystem: "gradle" + - package-ecosystem: "gomod" directory: "/" schedule: interval: "monthly" @@ -50,7 +50,7 @@ updates: labels: [] ignore: - dependency-name: "*" - - package-ecosystem: "cargo" + - package-ecosystem: "gradle" directory: "/" schedule: interval: "monthly" @@ -58,67 +58,38 @@ updates: labels: [] ignore: - dependency-name: "*" - - # ── Test fixture manifests (suppress all updates including security) ─ - # These directories contain intentionally pinned dependencies used as - # test fixtures. They must NOT be updated by dependabot. - - package-ecosystem: "maven" - directories: - - "/src/test/resources/tst_manifests/maven/**" - - "/src/test/resources/tst_manifests/it/maven/**" - schedule: - interval: "monthly" - labels: [] - ignore: - - dependency-name: "*" - - package-ecosystem: "npm" - directories: - - "/src/test/resources/tst_manifests/npm/**" - - "/src/test/resources/tst_manifests/pnpm/**" - - "/src/test/resources/tst_manifests/yarn-berry/**" - - "/src/test/resources/tst_manifests/yarn-classic/**" - - "/src/test/resources/tst_manifests/it/npm/**" - - "/src/test/resources/tst_manifests/it/pnpm/**" - - "/src/test/resources/tst_manifests/it/yarn/**" - schedule: - interval: "monthly" - labels: [] - ignore: - - dependency-name: "*" - - package-ecosystem: "pip" - directories: - - "/src/test/resources/tst_manifests/pip/**" - - "/src/test/resources/tst_manifests/it/pypi/**" - - "/src/test/resources/msc/python" - schedule: - interval: "monthly" - labels: [] - ignore: - - dependency-name: "*" - - package-ecosystem: "gomod" - directories: - - "/src/test/resources/tst_manifests/golang/**" - - "/src/test/resources/tst_manifests/it/golang/**" - - "/src/test/resources/msc/golang/**" - schedule: - interval: "monthly" - labels: [] - ignore: - - dependency-name: "*" - - package-ecosystem: "gradle" - directories: - - "/src/test/resources/tst_manifests/gradle-groovy/**" - - "/src/test/resources/tst_manifests/gradle-kotlin/**" - - "/src/test/resources/tst_manifests/it/gradle-groovy/**" - - "/src/test/resources/tst_manifests/it/gradle-kotlin/**" + - package-ecosystem: "cargo" + directory: "/" schedule: interval: "monthly" + open-pull-requests-limit: 0 labels: [] ignore: - dependency-name: "*" - - package-ecosystem: "cargo" + + # ── Test fixture manifests (maven only) ────────────────────────────── + # Maven is the production ecosystem so it cannot use a root-level + # ignore-all. Glob patterns (/**) don't reliably match nested + # subdirectories for security updates, so every directory containing + # a pom.xml is listed explicitly. Update this list when adding new + # test fixtures. + - package-ecosystem: "maven" directories: - - "/src/test/resources/tst_manifests/cargo/**" + - "/src/test/resources/tst_manifests/it/maven" + - "/src/test/resources/tst_manifests/maven/deps_no_trivial_with_ignore" + - "/src/test/resources/tst_manifests/maven/deps_with_ignore_on_artifact" + - "/src/test/resources/tst_manifests/maven/deps_with_ignore_on_dependency" + - "/src/test/resources/tst_manifests/maven/deps_with_ignore_on_group" + - "/src/test/resources/tst_manifests/maven/deps_with_ignore_on_version" + - "/src/test/resources/tst_manifests/maven/deps_with_ignore_on_wrong" + - "/src/test/resources/tst_manifests/maven/deps_with_no_ignore" + - "/src/test/resources/tst_manifests/maven/empty" + - "/src/test/resources/tst_manifests/maven/license/pom_with_empty_license" + - "/src/test/resources/tst_manifests/maven/license/pom_with_license" + - "/src/test/resources/tst_manifests/maven/license/pom_with_multiple_licenses" + - "/src/test/resources/tst_manifests/maven/license/pom_without_license" + - "/src/test/resources/tst_manifests/maven/pom_deps_with_no_ignore_common_paths" + - "/src/test/resources/tst_manifests/maven/pom_deps_with_no_ignore_provided_scope" schedule: interval: "monthly" labels: [] diff --git a/CONVENTIONS.md b/CONVENTIONS.md new file mode 100644 index 00000000..6af4be50 --- /dev/null +++ b/CONVENTIONS.md @@ -0,0 +1,100 @@ +# Coding Conventions + + + +## Language and Framework + +- **Primary Language**: Java 17 +- **Build Tool**: Maven 3.8+ +- **Module System**: Java 9+ modules (`module-info.java`) +- **Key Libraries**: Jackson 2.20.0 (JSON), CycloneDX 11.0.1 (SBOM), Jakarta Mail/Annotation APIs +- **Async Model**: `CompletableFuture` throughout the API + +## Code Style + +- **Formatter**: Spotless Maven Plugin with Google Java Format (GOOGLE style, `reflowLongStrings` enabled) +- **Indentation**: 2 spaces for Java, XML, YAML; 2 spaces for JSON +- **Line length**: 100 characters (120 for XML/FXML) +- **Line endings**: LF +- **Charset**: UTF-8 +- **License header**: Apache 2.0, automatically injected by Spotless +- **EditorConfig**: `.editorconfig` enforces formatting rules +- **Code coverage**: 81% unit test threshold (JaCoCo), 50 mutation threshold (PIT) + +## Naming Conventions + +- **Packages**: `io.github.guacsec.trustifyda.*` +- **Classes**: PascalCase + - Providers: `*Provider` (e.g., `JavaMavenProvider`, `PythonPipProvider`) + - Factories: `*Factory` (e.g., `JavaScriptProviderFactory`, `SbomFactory`) + - Abstract bases: `Base*Provider` (e.g., `BaseJavaProvider`) + - Utility classes: `final` with private constructor +- **Interfaces**: Simple names without `I` prefix (`Api`, `Provider`, `Sbom`) + - Inner static classes for related types: `Api.MediaType`, `Provider.Content` +- **Methods**: camelCase (`provideStack()`, `provideComponent()`, `getCustomPathOrElse()`) +- **Constants**: UPPER_SNAKE_CASE (`TRUSTIFY_DA_BACKEND_URL`, `CYCLONEDX_MEDIA_TYPE`) +- **Enums**: PascalCase class, UPPER_CASE values (`MAVEN`, `NPM`, `GRADLE`) +- **Test classes**: Snake_case pattern (`Operations_Test`, `Java_Maven_Provider_Test`) + +## File Organization + +``` +src/main/java/io/github/guacsec/trustifyda/ +├── Api.java # Main interface +├── Provider.java # Abstract provider contract +├── cli/ # CLI implementation +├── exception/ # Custom exceptions +├── image/ # Image analysis +├── impl/ # ExhortApi implementation, RequestManager +├── logging/ # LoggersFactory +├── providers/ # 18+ ecosystem provider implementations +├── sbom/ # SBOM handling (Sbom, SbomFactory, CycloneDXSbom) +├── tools/ # Utilities (Ecosystem, Operations) +├── utils/ # Environment, IgnorePatternDetector +└── vcs/ # Version control utilities +``` + +- Test resources: `src/test/resources/tst_manifests/` +- Integration tests: `src/it/` (Maven Invoker Plugin) + +## Error Handling + +- Runtime exceptions preferred over checked (e.g., `PackageNotInstalledException extends RuntimeException`) +- `CompletableFuture` for async operations with completion exception handling +- `IOException` allowed for file operations (checked exception) +- Environment validation at initialization time +- Logging via `LoggersFactory.getLogger()` (Java Logging API) + +## Testing Conventions + +- **Frameworks**: JUnit Jupiter 5, Mockito 5.17.0, AssertJ 3.27.3 +- **Extensions**: JUnit Pioneer (`@SetSystemProperty`, `@ClearSystemProperty`) +- **Assertions**: AssertJ fluent API (`assertThat()`, `assertThatRuntimeException()`) +- **Mocking**: Mockito (`@Mock`, `@InjectMocks`, `mockStatic()`, BDDMockito) +- **Test structure**: `@Nested` classes, `@ParameterizedTest @MethodSource` +- **Test naming**: Snake_case classes, descriptive methods (`when_running_process_for_existing_command_should_not_throw_exception`) +- **Test runner**: junit-platform-maven-plugin + +## Commit Messages + +- Follow Conventional Commits: `type(scope): description` +- DCO sign-off required +- Pull request titles must follow Conventional Commits format + +## Test Fixtures + +- **Dependabot suppression**: Test fixture directories contain intentionally pinned (sometimes vulnerable) dependencies. When adding a new test fixture directory with a manifest file, review `.github/dependabot.yml` to ensure the new path is covered. Non-maven ecosystems are suppressed via root-level `ignore: [{dependency-name: "*"}]` entries. Maven fixtures use per-directory entries with `/**` globs since maven is the production ecosystem; add the parent directory if a new maven fixture tree is introduced. + +## Test Fixtures + +- **Dependabot suppression**: Test fixture directories contain intentionally pinned (sometimes vulnerable) dependencies. When adding a new test fixture directory with a manifest file, review `.github/dependabot.yml` to ensure the new path is covered. Non-maven ecosystems are suppressed via root-level `ignore: [{dependency-name: "*"}]` entries. Maven fixtures use explicit per-directory entries since maven is the production ecosystem; add the directory path if a new maven fixture is introduced. + +## Dependencies + +- All versions in `` section: `{artifact-name}.version` pattern +- Compile scope: Jackson, Jakarta, CycloneDX, TOML parsers +- Test scope: JUnit, Mockito, AssertJ, JUnit Pioneer +- Maven Shade Plugin for CLI JAR with all dependencies +- Distribution to Maven Central (flatten plugin, OSSRH mode) +- GitHub Maven repository for `trustify-da-api-model` +- Enforcer plugin: dependency convergence, no circular dependencies From bd55dcccd72c84b5ad362c17f7a47605d01b25fc Mon Sep 17 00:00:00 2001 From: Ruben Romero Montes Date: Thu, 30 Apr 2026 14:17:48 +0200 Subject: [PATCH 2/2] chore(docs): add serena and conventions file Signed-off-by: Ruben Romero Montes --- .serena/.gitignore | 2 + .../go-modules-provider-architecture.md | 29 ++++ .serena/memories/project_overview.md | 31 +++++ .serena/project.yml | 127 ++++++++++++++++++ CONVENTIONS.md | 6 +- 5 files changed, 190 insertions(+), 5 deletions(-) create mode 100644 .serena/.gitignore create mode 100644 .serena/memories/go-modules-provider-architecture.md create mode 100644 .serena/memories/project_overview.md create mode 100644 .serena/project.yml diff --git a/.serena/.gitignore b/.serena/.gitignore new file mode 100644 index 00000000..2e510aff --- /dev/null +++ b/.serena/.gitignore @@ -0,0 +1,2 @@ +/cache +/project.local.yml diff --git a/.serena/memories/go-modules-provider-architecture.md b/.serena/memories/go-modules-provider-architecture.md new file mode 100644 index 00000000..de913254 --- /dev/null +++ b/.serena/memories/go-modules-provider-architecture.md @@ -0,0 +1,29 @@ +# GoModulesProvider Architecture + +## Dependency Resolution Flow +1. `getDependenciesSbom(Path, boolean)` orchestrates: runs `go mod graph`, `go mod edit -json`, determines main module version +2. `buildGoModulesDependencies(Path)` - runs `go mod graph` to get full dependency graph +3. `getDirectDependencyPaths(Path)` - runs `go mod edit -json` to identify direct vs indirect deps +4. For stack analysis: `buildSbomFromGraph` builds full dependency tree +5. For component analysis: `buildSbomFromList` lists only direct dependencies + +## Direct vs Indirect Filtering (TC-4300) +- Since Go 1.17, `go mod tidy` adds all transitively-imported modules to go.mod with `// indirect` marker +- `go mod graph` emits root-level edges for ALL modules in go.mod (both direct and indirect) +- `go mod edit -json` returns structured JSON with `Require` array where each entry has optional `Indirect: true` +- Both `buildSbomFromGraph` and `buildSbomFromList` filter root-level deps to only include direct ones + +## Key Gotcha: MVS Version Suffix +After MVS processing (`getFinalPackagesVersionsForModule`), the root key in the edges map changes from `module/path` to `module/path@v0.0.0`. The root comparison must use `getModulePath()` to strip the version suffix for both sides, otherwise the filtering silently does nothing when MVS is enabled. + +## Helper Methods +- `getModulePath(String)` - strips `@version` suffix from `go mod graph` entries (e.g., `github.com/foo/bar@v1.2.3` -> `github.com/foo/bar`) +- `extractPackageName(String)` - strips `//` comment from go.mod require lines (different purpose, don't confuse) +- `getParentVertex(String)` / `getChildVertex(String)` - split `go mod graph` edge lines on space +- `isGoToolchainEntry(String)` - filters out `go@` and `toolchain@` entries + +## Test Fixtures +- `src/test/resources/tst_manifests/golang/` - main test fixtures with 6 test folders +- `src/test/resources/msc/golang/mvs_logic/` - MVS-specific test fixtures +- Tests use `dropIgnoredKeepFormat()` to strip timestamps and goarch/goos before comparison +- Tests use `prettyJson()` (Jackson) to normalize JSON formatting diff --git a/.serena/memories/project_overview.md b/.serena/memories/project_overview.md new file mode 100644 index 00000000..931ebebc --- /dev/null +++ b/.serena/memories/project_overview.md @@ -0,0 +1,31 @@ +# exhort-java-api Project Overview + +## Purpose +Java client library for the Dependency Analytics (DA) / Exhort vulnerability analysis backend. Generates SBOMs (CycloneDX JSON) from project manifests and submits them for analysis. + +## Tech Stack +- Java 17+, Maven build +- Jackson for JSON, TOML parsing (com.moandjiezana.toml) +- XMLStreamReader for POM parsing +- CycloneDX SBOM model (custom `Sbom` class) + +## Key Directories +- `src/main/java/io/github/guacsec/trustifyda/providers/` - Provider implementations per ecosystem +- `src/main/java/io/github/guacsec/trustifyda/tools/` - Utilities (Operations, Ecosystem, Environment) +- `src/main/java/io/github/guacsec/trustifyda/utils/` - Shared utilities (IgnorePatternDetector, PythonControllerBase) +- `src/test/resources/tst_manifests/` - Test fixtures per ecosystem + +## Provider Pattern +Each ecosystem has a Provider class extending `Provider` abstract class with methods: +- `provideStack()` - Full dependency tree SBOM +- `provideComponent()` - Direct dependencies only SBOM +- `readLicenseFromManifest()` - License extraction +- `validateLockFile(Path)` - Lock file validation + +## Ignore Pattern Detection +Centralized in `IgnorePatternDetector` with constants `IGNORE_PATTERN` ("trustify-da-ignore") and `LEGACY_IGNORE_PATTERN` ("exhortignore"). + +## Commands +- Build: `mvn clean install` +- Test: `mvn test` +- Format: `mvn spotless:apply` diff --git a/.serena/project.yml b/.serena/project.yml new file mode 100644 index 00000000..6031978d --- /dev/null +++ b/.serena/project.yml @@ -0,0 +1,127 @@ +# the name by which the project can be referenced within Serena +project_name: "trustify-da-java-client" + + +# list of languages for which language servers are started; choose from: +# al ansible bash clojure cpp +# cpp_ccls crystal csharp csharp_omnisharp dart +# elixir elm erlang fortran fsharp +# go groovy haskell haxe hlsl +# java json julia kotlin lean4 +# lua luau markdown matlab msl +# nix ocaml pascal perl php +# php_phpactor powershell python python_jedi python_ty +# r rego ruby ruby_solargraph rust +# scala solidity swift systemverilog terraform +# toml typescript typescript_vts vue yaml +# zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# Note: +# - For C, use cpp +# - For JavaScript, use typescript +# - For Free Pascal/Lazarus, use pascal +# Special requirements: +# Some languages require additional setup/installations. +# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers +# When using multiple languages, the first language server that supports a given file will be used for that file. +# The first language is the default language and the respective language server will be used as a fallback. +# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +languages: +- java + +# the encoding used by text files in the project +# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings +encoding: "utf-8" + +# line ending convention to use when writing source files. +# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) +# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. +line_ending: + +# The language backend to use for this project. +# If not set, the global setting from serena_config.yml is used. +# Valid values: LSP, JetBrains +# Note: the backend is fixed at startup. If a project with a different backend +# is activated post-init, an error will be returned. +language_backend: + +# whether to use project's .gitignore files to ignore files +ignore_all_files_in_gitignore: true + +# list of additional paths to ignore in this project. +# Same syntax as gitignore, so you can use * and **. +# Note: global ignored_paths from serena_config.yml are also applied additively. +ignored_paths: [] + +# whether the project is in read-only mode +# If set to true, all editing tools will be disabled and attempts to use them will result in an error +# Added on 2025-04-18 +read_only: false + +# list of tool names to exclude. +# This extends the existing exclusions (e.g. from the global configuration) +# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html +excluded_tools: [] + +# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default). +# This extends the existing inclusions (e.g. from the global configuration). +# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html +included_optional_tools: [] + +# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. +# This cannot be combined with non-empty excluded_tools or included_optional_tools. +# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html +fixed_tools: [] + +# list of mode names to that are always to be included in the set of active modes +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this setting overrides the global configuration. +# Set this to [] to disable base modes for this project. +# Set this to a list of mode names to always include the respective modes for this project. +base_modes: + +# list of mode names that are to be activated by default, overriding the setting in the global configuration. +# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes. +# If the setting is undefined/empty, the default_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this overrides the setting from the global configuration (serena_config.yml). +# Therefore, you can set this to [] if you do not want the default modes defined in the global config to apply +# for this project. +# This setting can, in turn, be overridden by CLI parameters (--mode). +# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes +default_modes: + +# initial prompt for the project. It will always be given to the LLM upon activating the project +# (contrary to the memories, which are loaded on demand). +initial_prompt: "" + +# time budget (seconds) per tool call for the retrieval of additional symbol information +# such as docstrings or parameter information. +# This overrides the corresponding setting in the global configuration; see the documentation there. +# If null or missing, use the setting from the global configuration. +symbol_info_budget: + +# list of regex patterns which, when matched, mark a memory entry as read‑only. +# Extends the list from the global configuration, merging the two lists. +read_only_memory_patterns: [] + +# list of regex patterns for memories to completely ignore. +# Matching memories will not appear in list_memories or activate_project output +# and cannot be accessed via read_memory or write_memory. +# To access ignored memory files, use the read_file tool on the raw file path. +# Extends the list from the global configuration, merging the two lists. +# Example: ["_archive/.*", "_episodes/.*"] +ignored_memory_patterns: [] + +# advanced configuration option allowing to configure language server-specific options. +# Maps the language key to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. +# No documentation on options means no options are available. +ls_specific_settings: {} + +# list of mode names to be activated additionally for this project, e.g. ["query-projects"] +# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes. +# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes +added_modes: diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 6af4be50..66af48f9 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -1,6 +1,6 @@ # Coding Conventions - + ## Language and Framework @@ -85,10 +85,6 @@ src/main/java/io/github/guacsec/trustifyda/ - **Dependabot suppression**: Test fixture directories contain intentionally pinned (sometimes vulnerable) dependencies. When adding a new test fixture directory with a manifest file, review `.github/dependabot.yml` to ensure the new path is covered. Non-maven ecosystems are suppressed via root-level `ignore: [{dependency-name: "*"}]` entries. Maven fixtures use per-directory entries with `/**` globs since maven is the production ecosystem; add the parent directory if a new maven fixture tree is introduced. -## Test Fixtures - -- **Dependabot suppression**: Test fixture directories contain intentionally pinned (sometimes vulnerable) dependencies. When adding a new test fixture directory with a manifest file, review `.github/dependabot.yml` to ensure the new path is covered. Non-maven ecosystems are suppressed via root-level `ignore: [{dependency-name: "*"}]` entries. Maven fixtures use explicit per-directory entries since maven is the production ecosystem; add the directory path if a new maven fixture is introduced. - ## Dependencies - All versions in `` section: `{artifact-name}.version` pattern