evergreen-deprecation 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/src/capabilities.js +7 -5
- package/dist/src/cle-ingest.d.ts +41 -0
- package/dist/src/cle-ingest.js +300 -0
- package/dist/src/cli.js +64 -7
- package/dist/src/completions.js +2 -0
- package/dist/src/data-bundle.d.ts +1 -1
- package/dist/src/data-bundle.js +1 -1
- package/dist/src/detectors/container/index.js +260 -8
- package/dist/src/detectors/container/parse-dockerfile.d.ts +12 -5
- package/dist/src/detectors/container/parse-dockerfile.js +69 -8
- package/dist/src/detectors/container/rootfs.d.ts +63 -0
- package/dist/src/detectors/container/rootfs.js +463 -0
- package/dist/src/detectors/database/index.js +30 -6
- package/dist/src/detectors/database/parse-terraform.d.ts +38 -10
- package/dist/src/detectors/database/parse-terraform.js +204 -25
- package/dist/src/detectors/deno/index.d.ts +12 -0
- package/dist/src/detectors/deno/index.js +167 -0
- package/dist/src/detectors/deno/parse.d.ts +29 -0
- package/dist/src/detectors/deno/parse.js +125 -0
- package/dist/src/detectors/elixir/index.d.ts +14 -0
- package/dist/src/detectors/elixir/index.js +198 -0
- package/dist/src/detectors/elixir/parse-elixir-runtime.d.ts +29 -0
- package/dist/src/detectors/elixir/parse-elixir-runtime.js +60 -0
- package/dist/src/detectors/elixir/parse-mix-exs.d.ts +31 -0
- package/dist/src/detectors/elixir/parse-mix-exs.js +41 -0
- package/dist/src/detectors/elixir/parse-mix-lock.d.ts +24 -0
- package/dist/src/detectors/elixir/parse-mix-lock.js +36 -0
- package/dist/src/detectors/index.js +8 -0
- package/dist/src/detectors/kubernetes/index.d.ts +9 -0
- package/dist/src/detectors/kubernetes/index.js +123 -32
- package/dist/src/detectors/kubernetes/parse-helm.d.ts +32 -4
- package/dist/src/detectors/kubernetes/parse-helm.js +74 -17
- package/dist/src/detectors/kubernetes/parse-manifest.d.ts +1 -1
- package/dist/src/detectors/kubernetes/parse-manifest.js +22 -5
- package/dist/src/detectors/scala/index.d.ts +14 -0
- package/dist/src/detectors/scala/index.js +177 -0
- package/dist/src/detectors/scala/parse-build-properties.d.ts +20 -0
- package/dist/src/detectors/scala/parse-build-properties.js +35 -0
- package/dist/src/detectors/scala/parse-build-sbt.d.ts +19 -0
- package/dist/src/detectors/scala/parse-build-sbt.js +36 -0
- package/dist/src/detectors/scala/parse-scala-version.d.ts +21 -0
- package/dist/src/detectors/scala/parse-scala-version.js +31 -0
- package/dist/src/detectors/terraform/hcl.d.ts +15 -0
- package/dist/src/detectors/terraform/hcl.js +43 -0
- package/dist/src/detectors/terraform/index.d.ts +1 -1
- package/dist/src/detectors/terraform/index.js +154 -16
- package/dist/src/detectors/terraform/kubernetes.d.ts +46 -0
- package/dist/src/detectors/terraform/kubernetes.js +130 -0
- package/dist/src/detectors/terraform/plan.d.ts +36 -0
- package/dist/src/detectors/terraform/plan.js +193 -0
- package/dist/src/domain.d.ts +5 -0
- package/dist/src/engine.js +15 -5
- package/dist/src/export/cle.d.ts +39 -0
- package/dist/src/export/cle.js +56 -0
- package/dist/src/guard.d.ts +89 -0
- package/dist/src/guard.js +283 -0
- package/dist/src/lsp.d.ts +74 -0
- package/dist/src/lsp.js +667 -0
- package/dist/src/osv-advisories.d.ts +2 -0
- package/dist/src/osv-advisories.js +39 -0
- package/dist/src/renovate-preset.d.ts +27 -0
- package/dist/src/renovate-preset.js +158 -0
- package/dist/src/reporters.d.ts +34 -0
- package/dist/src/reporters.js +245 -0
- package/dist/src/sbom/cyclonedx.d.ts +16 -0
- package/dist/src/sbom/cyclonedx.js +107 -0
- package/dist/src/sbom/input.d.ts +26 -0
- package/dist/src/sbom/input.js +146 -0
- package/dist/src/sdk.d.ts +7 -0
- package/dist/src/usage/symbols.d.ts +1 -1
- package/dist/src/usage/tree-sitter.d.ts +1 -1
- package/dist/src/usage/tree-sitter.js +7 -6
- package/dist/src/usage/vendor/tree-sitter/LICENSE +21 -0
- package/dist/src/usage/vendor/tree-sitter/README.md +15 -0
- package/dist/src/usage/vendor/tree-sitter/tree-sitter-javascript.wasm +0 -0
- package/dist/src/usage/vendor/tree-sitter/tree-sitter-python.wasm +0 -0
- package/dist/src/usage/vendor/tree-sitter/tree-sitter-tsx.wasm +0 -0
- package/dist/src/usage/vendor/tree-sitter/tree-sitter-typescript.wasm +0 -0
- package/dist/src/usage/vendor/tree-sitter/tree-sitter.cjs +4075 -0
- package/dist/src/usage/vendor/tree-sitter/tree-sitter.d.ts +1027 -0
- package/dist/src/usage/vendor/tree-sitter/tree-sitter.wasm +0 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/examples/agents/copilot/.vscode/mcp.json +1 -1
- package/examples/agents/copilot/coding-agent-mcp.json +1 -1
- package/examples/agents/cursor/.cursor/mcp.json +1 -1
- package/examples/agents/gemini/gemini-extension.json +2 -2
- package/examples/agents/gemini/settings.json +1 -1
- package/examples/agents/kilo/kilo.jsonc +1 -1
- package/examples/capabilities.json +12 -10
- package/examples/lifecycle-watch.yml +1 -1
- package/package.json +8 -5
- package/registry/container.json +269 -0
- package/registry/database.json +410 -0
- package/registry/deno.json +177 -0
- package/registry/elixir.json +309 -0
- package/registry/java.json +164 -0
- package/registry/kubernetes.json +114 -0
- package/registry/osv.json +66 -0
- package/registry/scala.json +191 -0
- package/registry/terraform.json +1037 -0
- package/schemas/finding.v1.schema.json +10 -0
- package/schemas/scan.v1.schema.json +10 -0
- package/dist/src/action.js.map +0 -1
- package/dist/src/agent-instructions.js.map +0 -1
- package/dist/src/agent-tools.js.map +0 -1
- package/dist/src/as-of.js.map +0 -1
- package/dist/src/badge.js.map +0 -1
- package/dist/src/baseline.js.map +0 -1
- package/dist/src/capabilities.js.map +0 -1
- package/dist/src/check-dependency.js.map +0 -1
- package/dist/src/cli.js.map +0 -1
- package/dist/src/codeowners.js.map +0 -1
- package/dist/src/completions.js.map +0 -1
- package/dist/src/conformance/checks/bounds.js.map +0 -1
- package/dist/src/conformance/checks/determinism.js.map +0 -1
- package/dist/src/conformance/checks/evidence.js.map +0 -1
- package/dist/src/conformance/checks/inputs.js.map +0 -1
- package/dist/src/conformance/checks/locations.js.map +0 -1
- package/dist/src/conformance/checks/malformed.js.map +0 -1
- package/dist/src/conformance/checks/zero.js.map +0 -1
- package/dist/src/conformance/index.js.map +0 -1
- package/dist/src/conformance/trace-reads.js.map +0 -1
- package/dist/src/data-bundle.js.map +0 -1
- package/dist/src/data-signature.js.map +0 -1
- package/dist/src/data-source.js.map +0 -1
- package/dist/src/data-status.js.map +0 -1
- package/dist/src/data-update.js.map +0 -1
- package/dist/src/detector-inputs.js.map +0 -1
- package/dist/src/detectors/android/gradle-scan.js.map +0 -1
- package/dist/src/detectors/android/index.js.map +0 -1
- package/dist/src/detectors/android/parse-build-gradle.js.map +0 -1
- package/dist/src/detectors/android/parse-libs-versions-toml.js.map +0 -1
- package/dist/src/detectors/android/parse-settings-gradle.js.map +0 -1
- package/dist/src/detectors/ci-images/index.js.map +0 -1
- package/dist/src/detectors/ci-images/parse-azure-pipelines.js.map +0 -1
- package/dist/src/detectors/ci-images/parse-circleci.js.map +0 -1
- package/dist/src/detectors/ci-images/parse-gitlab-ci.js.map +0 -1
- package/dist/src/detectors/container/index.js.map +0 -1
- package/dist/src/detectors/container/parse-compose.js.map +0 -1
- package/dist/src/detectors/container/parse-dockerfile.js.map +0 -1
- package/dist/src/detectors/container/parse-image.js.map +0 -1
- package/dist/src/detectors/cpp/index.js.map +0 -1
- package/dist/src/detectors/cpp/parse-cmakelists.js.map +0 -1
- package/dist/src/detectors/cpp/parse-conan.js.map +0 -1
- package/dist/src/detectors/cpp/parse-vcpkg.js.map +0 -1
- package/dist/src/detectors/dart/index.js.map +0 -1
- package/dist/src/detectors/dart/parse-fvm.js.map +0 -1
- package/dist/src/detectors/dart/parse-pubspec-lock.js.map +0 -1
- package/dist/src/detectors/dart/parse-pubspec.js.map +0 -1
- package/dist/src/detectors/database/index.js.map +0 -1
- package/dist/src/detectors/database/parse-compose.js.map +0 -1
- package/dist/src/detectors/database/parse-terraform.js.map +0 -1
- package/dist/src/detectors/dotnet/index.js.map +0 -1
- package/dist/src/detectors/dotnet/msbuild-xml.js.map +0 -1
- package/dist/src/detectors/dotnet/parse-global-json.js.map +0 -1
- package/dist/src/detectors/dotnet/parse-msbuild.js.map +0 -1
- package/dist/src/detectors/dotnet/parse-packages-lock.js.map +0 -1
- package/dist/src/detectors/github-actions-dotnet.js.map +0 -1
- package/dist/src/detectors/github-actions-node.js.map +0 -1
- package/dist/src/detectors/github-actions.js.map +0 -1
- package/dist/src/detectors/go/index.js.map +0 -1
- package/dist/src/detectors/go/parse-go-mod.js.map +0 -1
- package/dist/src/detectors/go/parse-go-work.js.map +0 -1
- package/dist/src/detectors/index.js.map +0 -1
- package/dist/src/detectors/java/index.js.map +0 -1
- package/dist/src/detectors/java/jdk-version.js.map +0 -1
- package/dist/src/detectors/java/parse-gradle.js.map +0 -1
- package/dist/src/detectors/java/parse-pins.js.map +0 -1
- package/dist/src/detectors/java/parse-pom.js.map +0 -1
- package/dist/src/detectors/java/parse-wrapper.js.map +0 -1
- package/dist/src/detectors/java/xml.js.map +0 -1
- package/dist/src/detectors/json-lines.js.map +0 -1
- package/dist/src/detectors/kubernetes/api-version.js.map +0 -1
- package/dist/src/detectors/kubernetes/index.js.map +0 -1
- package/dist/src/detectors/kubernetes/parse-helm.js.map +0 -1
- package/dist/src/detectors/kubernetes/parse-kustomize.js.map +0 -1
- package/dist/src/detectors/kubernetes/parse-manifest.js.map +0 -1
- package/dist/src/detectors/npm.js.map +0 -1
- package/dist/src/detectors/php/index.js.map +0 -1
- package/dist/src/detectors/php/parse-composer-json.js.map +0 -1
- package/dist/src/detectors/php/parse-composer-lock.js.map +0 -1
- package/dist/src/detectors/php/parse-php-runtime.js.map +0 -1
- package/dist/src/detectors/python.js.map +0 -1
- package/dist/src/detectors/ruby/index.js.map +0 -1
- package/dist/src/detectors/ruby/parse-gemfile-lock.js.map +0 -1
- package/dist/src/detectors/ruby/parse-gemfile.js.map +0 -1
- package/dist/src/detectors/ruby/parse-gemspec.js.map +0 -1
- package/dist/src/detectors/ruby/parse-ruby-runtime.js.map +0 -1
- package/dist/src/detectors/rust/index.js.map +0 -1
- package/dist/src/detectors/rust/parse-cargo-lock.js.map +0 -1
- package/dist/src/detectors/rust/parse-cargo-toml.js.map +0 -1
- package/dist/src/detectors/rust/parse-rust-toolchain.js.map +0 -1
- package/dist/src/detectors/rust/toml-lines.js.map +0 -1
- package/dist/src/detectors/serverless/index.js.map +0 -1
- package/dist/src/detectors/serverless/parse-serverless-yml.js.map +0 -1
- package/dist/src/detectors/serverless/parse-template.js.map +0 -1
- package/dist/src/detectors/serverless/parse-tf.js.map +0 -1
- package/dist/src/detectors/shell/index.js.map +0 -1
- package/dist/src/detectors/shell/parse-powershell.js.map +0 -1
- package/dist/src/detectors/shell/parse-workflow.js.map +0 -1
- package/dist/src/detectors/swift/index.js.map +0 -1
- package/dist/src/detectors/swift/parse-package-resolved.js.map +0 -1
- package/dist/src/detectors/swift/parse-package-swift.js.map +0 -1
- package/dist/src/detectors/swift/parse-swift-toolchain.js.map +0 -1
- package/dist/src/detectors/swift/parse-xcode-project.js.map +0 -1
- package/dist/src/detectors/terraform/hcl.js.map +0 -1
- package/dist/src/detectors/terraform/index.js.map +0 -1
- package/dist/src/detectors/terraform/parse-lock.js.map +0 -1
- package/dist/src/detectors/terraform/parse-terraform.js.map +0 -1
- package/dist/src/detectors/terraform/parse-version-files.js.map +0 -1
- package/dist/src/diff.js.map +0 -1
- package/dist/src/doctor.js.map +0 -1
- package/dist/src/domain.js.map +0 -1
- package/dist/src/engine.js.map +0 -1
- package/dist/src/exposure.js.map +0 -1
- package/dist/src/fix/codemods/index.js.map +0 -1
- package/dist/src/fix/git.js.map +0 -1
- package/dist/src/fix/github-actions.js.map +0 -1
- package/dist/src/fix/index.js.map +0 -1
- package/dist/src/fix/runtime-pins.js.map +0 -1
- package/dist/src/fix/verify.js.map +0 -1
- package/dist/src/forecast.js.map +0 -1
- package/dist/src/github-action.js.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/init-agents.js.map +0 -1
- package/dist/src/init-hooks.js.map +0 -1
- package/dist/src/init-personal.js.map +0 -1
- package/dist/src/mcp-cli.js.map +0 -1
- package/dist/src/mcp-server.js.map +0 -1
- package/dist/src/mcp.js.map +0 -1
- package/dist/src/migration-packet.js.map +0 -1
- package/dist/src/migration-prompt.js.map +0 -1
- package/dist/src/min-support.js.map +0 -1
- package/dist/src/package-files.js.map +0 -1
- package/dist/src/paths.js.map +0 -1
- package/dist/src/pr-comment.js.map +0 -1
- package/dist/src/purl.js.map +0 -1
- package/dist/src/registry.js.map +0 -1
- package/dist/src/report-issue.js.map +0 -1
- package/dist/src/reporters.js.map +0 -1
- package/dist/src/rollup.js.map +0 -1
- package/dist/src/rule-resources.js.map +0 -1
- package/dist/src/runtime-pin.js.map +0 -1
- package/dist/src/sdk.js.map +0 -1
- package/dist/src/setup.js.map +0 -1
- package/dist/src/source-usage.js.map +0 -1
- package/dist/src/status-file.js.map +0 -1
- package/dist/src/tui.js.map +0 -1
- package/dist/src/usage/javascript.js.map +0 -1
- package/dist/src/usage/python.js.map +0 -1
- package/dist/src/usage/symbols.js.map +0 -1
- package/dist/src/usage/tree-sitter.js.map +0 -1
- package/dist/src/usage-log.js.map +0 -1
- package/dist/src/vers/gem.js.map +0 -1
- package/dist/src/vers/index.js.map +0 -1
- package/dist/src/vers/maven.js.map +0 -1
- package/dist/src/vers/nuget.js.map +0 -1
- package/dist/src/vers/pep440.js.map +0 -1
- package/dist/src/vers/semver.js.map +0 -1
- package/dist/src/vers.js.map +0 -1
- package/dist/src/version.js.map +0 -1
- package/dist/src/watch.js.map +0 -1
- package/dist/src/workspaces.js.map +0 -1
- package/examples/demo/.github/workflows/ci.yml +0 -10
- package/examples/demo/package.json +0 -10
- package/examples/demo/src/client.js +0 -5
- package/skills/evergreen-deprecation/evals/cases.json +0 -67
- package/skills/evergreen-deprecation/evals/dependency-cases.json +0 -389
package/README.md
CHANGED
package/dist/src/capabilities.js
CHANGED
|
@@ -80,6 +80,7 @@ const manifest = {
|
|
|
80
80
|
{ id: "cpp", status: "experimental", maturity: "experimental", detectorIds: ["cpp"] },
|
|
81
81
|
{ id: "dart", status: "experimental", maturity: "experimental", detectorIds: ["dart"] },
|
|
82
82
|
{ id: "dotnet", status: "supported", maturity: "beta", detectorIds: ["dotnet"] },
|
|
83
|
+
{ id: "elixir", status: "experimental", maturity: "experimental", detectorIds: ["elixir"] },
|
|
83
84
|
{ id: "github-actions", status: "supported", maturity: "stable", detectorIds: ["github-actions"] },
|
|
84
85
|
{ id: "go", status: "experimental", maturity: "experimental", detectorIds: ["go"] },
|
|
85
86
|
{ id: "java", status: "supported", maturity: "beta", detectorIds: ["java"] },
|
|
@@ -91,6 +92,7 @@ const manifest = {
|
|
|
91
92
|
{ id: "python", status: "supported", maturity: "stable", detectorIds: ["python"] },
|
|
92
93
|
{ id: "ruby", status: "experimental", maturity: "experimental", detectorIds: ["ruby"] },
|
|
93
94
|
{ id: "rust", status: "experimental", maturity: "experimental", detectorIds: ["rust"] },
|
|
95
|
+
{ id: "scala", status: "experimental", maturity: "experimental", detectorIds: ["scala"] },
|
|
94
96
|
{ id: "serverless", status: "supported", maturity: "beta", detectorIds: ["serverless"] },
|
|
95
97
|
{ id: "swift", status: "supported", maturity: "beta", detectorIds: ["swift"] },
|
|
96
98
|
{ id: "terraform", status: "experimental", maturity: "experimental", detectorIds: ["terraform"] },
|
|
@@ -103,35 +105,35 @@ const manifest = {
|
|
|
103
105
|
status: "supported",
|
|
104
106
|
version: "1",
|
|
105
107
|
description: "Scan a local repository with all built-in lifecycle detectors.",
|
|
106
|
-
ecosystems: ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "php", "python", "ruby", "rust", "serverless", "shell", "swift", "terraform"],
|
|
108
|
+
ecosystems: ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "elixir", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "php", "python", "ruby", "rust", "scala", "serverless", "shell", "swift", "terraform"],
|
|
107
109
|
},
|
|
108
110
|
{
|
|
109
111
|
id: "deprecation.list",
|
|
110
112
|
status: "supported",
|
|
111
113
|
version: "1",
|
|
112
114
|
description: "List compact lifecycle findings from a fresh local scan.",
|
|
113
|
-
ecosystems: ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "php", "python", "ruby", "rust", "serverless", "shell", "swift", "terraform"],
|
|
115
|
+
ecosystems: ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "elixir", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "php", "python", "ruby", "rust", "scala", "serverless", "shell", "swift", "terraform"],
|
|
114
116
|
},
|
|
115
117
|
{
|
|
116
118
|
id: "deprecation.explain",
|
|
117
119
|
status: "supported",
|
|
118
120
|
version: "1",
|
|
119
121
|
description: "Explain one evidence-backed finding by its exact identifier.",
|
|
120
|
-
ecosystems: ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "php", "python", "ruby", "rust", "serverless", "shell", "swift", "terraform"],
|
|
122
|
+
ecosystems: ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "elixir", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "php", "python", "ruby", "rust", "scala", "serverless", "shell", "swift", "terraform"],
|
|
121
123
|
},
|
|
122
124
|
{
|
|
123
125
|
id: "migration.context.create",
|
|
124
126
|
status: "supported",
|
|
125
127
|
version: "1",
|
|
126
128
|
description: "Create a validated migration packet for one finding.",
|
|
127
|
-
ecosystems: ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "php", "python", "ruby", "rust", "serverless", "shell", "swift", "terraform"],
|
|
129
|
+
ecosystems: ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "elixir", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "php", "python", "ruby", "rust", "scala", "serverless", "shell", "swift", "terraform"],
|
|
128
130
|
},
|
|
129
131
|
{
|
|
130
132
|
id: "migration.verify",
|
|
131
133
|
status: "supported",
|
|
132
134
|
version: "1",
|
|
133
135
|
description: "Re-scan and verify that an observed finding is absent.",
|
|
134
|
-
ecosystems: ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "php", "python", "ruby", "rust", "serverless", "shell", "swift", "terraform"],
|
|
136
|
+
ecosystems: ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "elixir", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "php", "python", "ruby", "rust", "scala", "serverless", "shell", "swift", "terraform"],
|
|
135
137
|
},
|
|
136
138
|
{
|
|
137
139
|
id: "environment.doctor",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Detector, DetectorContext, DetectorFinding, DetectorInputs } from "./sdk.js";
|
|
2
|
+
/** File name Evergreen looks for at the root of an installed package. */
|
|
3
|
+
export declare const CLE_FILE_NAME = "cle.json";
|
|
4
|
+
interface CleVersionSpec {
|
|
5
|
+
range: string;
|
|
6
|
+
}
|
|
7
|
+
interface CleEventRecord {
|
|
8
|
+
id: number;
|
|
9
|
+
type: string;
|
|
10
|
+
effective: string;
|
|
11
|
+
published: string;
|
|
12
|
+
versions?: CleVersionSpec[];
|
|
13
|
+
description?: string;
|
|
14
|
+
references?: string[];
|
|
15
|
+
}
|
|
16
|
+
interface CleDocumentRecord {
|
|
17
|
+
$schema: string;
|
|
18
|
+
identifier: string | string[];
|
|
19
|
+
updatedAt: string;
|
|
20
|
+
events: CleEventRecord[];
|
|
21
|
+
}
|
|
22
|
+
export interface CleIngestOptions {
|
|
23
|
+
/**
|
|
24
|
+
* ISO-8601 scan time used to decide scheduled vs reached severity (decision 0010).
|
|
25
|
+
* Callers must pass the same timestamp the scan uses; do not read the clock here.
|
|
26
|
+
*/
|
|
27
|
+
readonly scannedAt: string;
|
|
28
|
+
}
|
|
29
|
+
/** Parses a CLE 1.0 document without inventing lifecycle claims. Malformed input yields undefined. */
|
|
30
|
+
export declare function parseCleDocument(input: unknown): CleDocumentRecord | undefined;
|
|
31
|
+
/** Findings from maintainer CLE documents shipped inside installed packages. Offline only. */
|
|
32
|
+
export declare function ingestCle(root: string, options: CleIngestOptions): Promise<DetectorFinding[]>;
|
|
33
|
+
/** Reads CLE documents from installed packages during a repository scan. */
|
|
34
|
+
export declare class CleIngestDetector implements Detector {
|
|
35
|
+
readonly apiVersion: "evergreen.detector.v1";
|
|
36
|
+
readonly id = "cle";
|
|
37
|
+
readonly inputs: DetectorInputs;
|
|
38
|
+
scan(context: DetectorContext): Promise<readonly DetectorFinding[]>;
|
|
39
|
+
}
|
|
40
|
+
export declare const cleDetector: CleIngestDetector;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { CLE_SCHEMA } from "./export/cle.js";
|
|
5
|
+
import { relativePosix } from "./paths.js";
|
|
6
|
+
import { parsePurl, purlIdentity, PurlError } from "./purl.js";
|
|
7
|
+
import { DETECTOR_API_VERSION } from "./sdk.js";
|
|
8
|
+
import { versContains } from "./vers/index.js";
|
|
9
|
+
/** File name Evergreen looks for at the root of an installed package. */
|
|
10
|
+
export const CLE_FILE_NAME = "cle.json";
|
|
11
|
+
const REPORTABLE = new Set(["endOfSupport", "endOfLife", "endOfDevelopment", "endOfDistribution"]);
|
|
12
|
+
const REACHED_SEVERITY = {
|
|
13
|
+
endOfLife: "high",
|
|
14
|
+
endOfDistribution: "high",
|
|
15
|
+
endOfSupport: "medium",
|
|
16
|
+
endOfDevelopment: "medium",
|
|
17
|
+
};
|
|
18
|
+
const TITLE = {
|
|
19
|
+
endOfLife: "End of life",
|
|
20
|
+
endOfDistribution: "End of distribution",
|
|
21
|
+
endOfSupport: "End of support",
|
|
22
|
+
endOfDevelopment: "End of development",
|
|
23
|
+
};
|
|
24
|
+
const SKIPPED_NODE_MODULES = new Set([".bin", ".cache"]);
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
function isNonEmptyString(value) {
|
|
29
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
30
|
+
}
|
|
31
|
+
/** Parses a CLE 1.0 document without inventing lifecycle claims. Malformed input yields undefined. */
|
|
32
|
+
export function parseCleDocument(input) {
|
|
33
|
+
if (!isRecord(input) || input.$schema !== CLE_SCHEMA)
|
|
34
|
+
return undefined;
|
|
35
|
+
if (!isNonEmptyString(input.updatedAt) || !Array.isArray(input.events))
|
|
36
|
+
return undefined;
|
|
37
|
+
const identifiers = Array.isArray(input.identifier)
|
|
38
|
+
? input.identifier.filter(isNonEmptyString)
|
|
39
|
+
: isNonEmptyString(input.identifier) ? [input.identifier] : [];
|
|
40
|
+
if (identifiers.length === 0)
|
|
41
|
+
return undefined;
|
|
42
|
+
const events = [];
|
|
43
|
+
for (const raw of input.events) {
|
|
44
|
+
if (!isRecord(raw) || typeof raw.id !== "number" || !Number.isInteger(raw.id) || raw.id < 1)
|
|
45
|
+
return undefined;
|
|
46
|
+
if (!isNonEmptyString(raw.type) || !isNonEmptyString(raw.effective) || !isNonEmptyString(raw.published))
|
|
47
|
+
return undefined;
|
|
48
|
+
const event = {
|
|
49
|
+
id: raw.id,
|
|
50
|
+
type: raw.type,
|
|
51
|
+
effective: raw.effective,
|
|
52
|
+
published: raw.published,
|
|
53
|
+
};
|
|
54
|
+
if (raw.versions !== undefined) {
|
|
55
|
+
if (!Array.isArray(raw.versions) || raw.versions.length === 0)
|
|
56
|
+
return undefined;
|
|
57
|
+
const versions = [];
|
|
58
|
+
for (const spec of raw.versions) {
|
|
59
|
+
if (!isRecord(spec) || !isNonEmptyString(spec.range))
|
|
60
|
+
return undefined;
|
|
61
|
+
versions.push({ range: spec.range });
|
|
62
|
+
}
|
|
63
|
+
event.versions = versions;
|
|
64
|
+
}
|
|
65
|
+
if (raw.description !== undefined) {
|
|
66
|
+
if (!isNonEmptyString(raw.description))
|
|
67
|
+
return undefined;
|
|
68
|
+
event.description = raw.description;
|
|
69
|
+
}
|
|
70
|
+
if (raw.references !== undefined) {
|
|
71
|
+
if (!Array.isArray(raw.references) || !raw.references.every(isNonEmptyString))
|
|
72
|
+
return undefined;
|
|
73
|
+
event.references = raw.references;
|
|
74
|
+
}
|
|
75
|
+
events.push(event);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
$schema: CLE_SCHEMA,
|
|
79
|
+
identifier: identifiers.length === 1 ? identifiers[0] : identifiers,
|
|
80
|
+
updatedAt: input.updatedAt,
|
|
81
|
+
events,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function deadlineOf(effective) {
|
|
85
|
+
const match = /^(\d{4}-\d{2}-\d{2})/.exec(effective);
|
|
86
|
+
return match?.[1];
|
|
87
|
+
}
|
|
88
|
+
/** True when the event's effective time is still in the future relative to the scan timestamp. */
|
|
89
|
+
function isScheduled(effective, scannedAt) {
|
|
90
|
+
const effectiveMs = Date.parse(effective);
|
|
91
|
+
const scannedMs = Date.parse(scannedAt);
|
|
92
|
+
if (!Number.isFinite(effectiveMs) || !Number.isFinite(scannedMs))
|
|
93
|
+
return false;
|
|
94
|
+
return effectiveMs > scannedMs;
|
|
95
|
+
}
|
|
96
|
+
function packagePurl(name) {
|
|
97
|
+
if (!name || name.startsWith(".") || name.includes("\\") || name.includes(".."))
|
|
98
|
+
return undefined;
|
|
99
|
+
try {
|
|
100
|
+
if (name.startsWith("@")) {
|
|
101
|
+
const slash = name.indexOf("/");
|
|
102
|
+
if (slash <= 1 || slash === name.length - 1)
|
|
103
|
+
return undefined;
|
|
104
|
+
return purlIdentity(`pkg:npm/${name.slice(1, slash)}/${name.slice(slash + 1)}`);
|
|
105
|
+
}
|
|
106
|
+
return purlIdentity(`pkg:npm/${name}`);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (error instanceof PurlError)
|
|
110
|
+
return undefined;
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function identifiersMatch(document, packageIdentity) {
|
|
115
|
+
const listed = Array.isArray(document.identifier) ? document.identifier : [document.identifier];
|
|
116
|
+
return listed.some((value) => {
|
|
117
|
+
try {
|
|
118
|
+
return purlIdentity(value) === packageIdentity;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (error instanceof PurlError)
|
|
122
|
+
return false;
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function eventApplies(event, version) {
|
|
128
|
+
if (!REPORTABLE.has(event.type))
|
|
129
|
+
return false;
|
|
130
|
+
if (!event.versions?.length)
|
|
131
|
+
return false;
|
|
132
|
+
return event.versions.some((spec) => {
|
|
133
|
+
try {
|
|
134
|
+
return versContains(spec.range, version);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function sourcesFor(clePath, event) {
|
|
142
|
+
const sources = [
|
|
143
|
+
{ title: "Maintainer CLE declaration", url: pathToFileURL(clePath).href },
|
|
144
|
+
];
|
|
145
|
+
for (const reference of event.references ?? []) {
|
|
146
|
+
if (/^https?:\/\//i.test(reference))
|
|
147
|
+
sources.push({ title: "CLE event reference", url: reference });
|
|
148
|
+
}
|
|
149
|
+
return sources;
|
|
150
|
+
}
|
|
151
|
+
function ruleIdFor(packageIdentity, event) {
|
|
152
|
+
const slug = packageIdentity.replace(/^pkg:/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
153
|
+
return `cle.${event.type}.${slug}.${event.id}`;
|
|
154
|
+
}
|
|
155
|
+
function findingFor(root, clePath, packageIdentity, version, event, scannedAt) {
|
|
156
|
+
const type = event.type;
|
|
157
|
+
const path = relativePosix(root, clePath);
|
|
158
|
+
const deadline = deadlineOf(event.effective);
|
|
159
|
+
const scheduled = isScheduled(event.effective, scannedAt);
|
|
160
|
+
const title = scheduled
|
|
161
|
+
? `${TITLE[type]} scheduled (maintainer CLE)`
|
|
162
|
+
: `${TITLE[type]} (maintainer CLE)`;
|
|
163
|
+
const detail = event.description?.trim() || `${packageIdentity}@${version} matches a maintainer-declared ${type} event.`;
|
|
164
|
+
const when = deadline
|
|
165
|
+
? scheduled ? ` Scheduled for ${deadline}.` : ` Effective ${deadline}.`
|
|
166
|
+
: "";
|
|
167
|
+
const ruleId = ruleIdFor(packageIdentity, event);
|
|
168
|
+
return {
|
|
169
|
+
id: `${ruleId}:${path}:1`,
|
|
170
|
+
ruleId,
|
|
171
|
+
title,
|
|
172
|
+
summary: `${detail}${when}`,
|
|
173
|
+
severity: scheduled ? "low" : REACHED_SEVERITY[type],
|
|
174
|
+
location: { path, line: 1 },
|
|
175
|
+
...(deadline ? { deadline } : {}),
|
|
176
|
+
sources: sourcesFor(clePath, event),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async function readJson(path) {
|
|
180
|
+
try {
|
|
181
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
if (error.code === "ENOENT")
|
|
185
|
+
return undefined;
|
|
186
|
+
if (error instanceof SyntaxError)
|
|
187
|
+
return undefined;
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Installed packages that ship a parseable CLE document next to package.json.
|
|
193
|
+
* Reads `cle.json` first so ordinary packages cost one failed read; only then opens package.json.
|
|
194
|
+
*/
|
|
195
|
+
async function clePackages(root) {
|
|
196
|
+
const found = [];
|
|
197
|
+
async function walkNodeModules(directory) {
|
|
198
|
+
let entries;
|
|
199
|
+
try {
|
|
200
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
206
|
+
if (entry.isSymbolicLink() || SKIPPED_NODE_MODULES.has(entry.name))
|
|
207
|
+
continue;
|
|
208
|
+
if (!entry.isDirectory())
|
|
209
|
+
continue;
|
|
210
|
+
const path = join(directory, entry.name);
|
|
211
|
+
if (entry.name === ".pnpm") {
|
|
212
|
+
let stores;
|
|
213
|
+
try {
|
|
214
|
+
stores = await readdir(path, { withFileTypes: true });
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
for (const store of stores.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
220
|
+
if (store.isSymbolicLink() || !store.isDirectory())
|
|
221
|
+
continue;
|
|
222
|
+
await walkNodeModules(join(path, store.name, "node_modules"));
|
|
223
|
+
}
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (entry.name.startsWith("@")) {
|
|
227
|
+
let scoped;
|
|
228
|
+
try {
|
|
229
|
+
scoped = await readdir(path, { withFileTypes: true });
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
for (const child of scoped.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
235
|
+
if (child.isSymbolicLink() || !child.isDirectory())
|
|
236
|
+
continue;
|
|
237
|
+
await considerPackage(join(path, child.name));
|
|
238
|
+
}
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
await considerPackage(path);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
async function considerPackage(directory) {
|
|
245
|
+
const clePath = join(directory, CLE_FILE_NAME);
|
|
246
|
+
const document = parseCleDocument(await readJson(clePath));
|
|
247
|
+
if (document) {
|
|
248
|
+
const manifest = await readJson(join(directory, "package.json"));
|
|
249
|
+
if (isRecord(manifest))
|
|
250
|
+
found.push({ directory, clePath, document, manifest });
|
|
251
|
+
}
|
|
252
|
+
await walkNodeModules(join(directory, "node_modules"));
|
|
253
|
+
}
|
|
254
|
+
await walkNodeModules(join(root, "node_modules"));
|
|
255
|
+
return found.sort((left, right) => left.directory.localeCompare(right.directory));
|
|
256
|
+
}
|
|
257
|
+
/** Findings from maintainer CLE documents shipped inside installed packages. Offline only. */
|
|
258
|
+
export async function ingestCle(root, options) {
|
|
259
|
+
const findings = [];
|
|
260
|
+
for (const { clePath, document, manifest } of await clePackages(root)) {
|
|
261
|
+
if (!isNonEmptyString(manifest.name) || !isNonEmptyString(manifest.version))
|
|
262
|
+
continue;
|
|
263
|
+
const packageIdentity = packagePurl(manifest.name);
|
|
264
|
+
if (!packageIdentity || !identifiersMatch(document, packageIdentity))
|
|
265
|
+
continue;
|
|
266
|
+
// CLE may include a version on the identifier; identity comparison already strips it.
|
|
267
|
+
try {
|
|
268
|
+
for (const value of Array.isArray(document.identifier) ? document.identifier : [document.identifier]) {
|
|
269
|
+
parsePurl(value);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
if (error instanceof PurlError)
|
|
274
|
+
continue;
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
for (const event of document.events) {
|
|
278
|
+
if (!eventApplies(event, manifest.version))
|
|
279
|
+
continue;
|
|
280
|
+
findings.push(findingFor(root, clePath, packageIdentity, manifest.version, event, options.scannedAt));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return findings.sort((left, right) => left.id.localeCompare(right.id));
|
|
284
|
+
}
|
|
285
|
+
/** Reads CLE documents from installed packages during a repository scan. */
|
|
286
|
+
export class CleIngestDetector {
|
|
287
|
+
apiVersion = DETECTOR_API_VERSION;
|
|
288
|
+
id = "cle";
|
|
289
|
+
inputs = {
|
|
290
|
+
fileNames: [CLE_FILE_NAME, "package.json"],
|
|
291
|
+
directories: ["node_modules"],
|
|
292
|
+
};
|
|
293
|
+
async scan(context) {
|
|
294
|
+
// The engine always passes scannedAt. Harnesses that call scan({ root }) omit it; without a
|
|
295
|
+
// timestamp every matching event is treated as reached so we never read the clock here.
|
|
296
|
+
return ingestCle(context.root, { scannedAt: context.scannedAt ?? "9999-12-31T23:59:59.999Z" });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
export const cleDetector = new CleIngestDetector();
|
|
300
|
+
//# sourceMappingURL=cle-ingest.js.map
|
package/dist/src/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import { AgentFileError } from "./agent-instructions.js";
|
|
|
15
15
|
import { configureActiveData, DataPinError, getActiveData } from "./data-source.js";
|
|
16
16
|
import { DataUpdateError, renderDataUpdateText, updateData } from "./data-update.js";
|
|
17
17
|
import { getDataStatus, renderDataStatusText } from "./data-status.js";
|
|
18
|
+
import { guardCommand, GuardUsageError } from "./guard.js";
|
|
18
19
|
import { initAgents } from "./init-agents.js";
|
|
19
20
|
import { initHooks } from "./init-hooks.js";
|
|
20
21
|
import { hasPersonalConfig, initPersonal } from "./init-personal.js";
|
|
@@ -32,17 +33,20 @@ import { readPinnedRuntimes } from "./runtime-pin.js";
|
|
|
32
33
|
import { DiffError, diffRefs, renderDiffText } from "./diff.js";
|
|
33
34
|
import { groupScanByOwner, renderOwnerGroupsText } from "./codeowners.js";
|
|
34
35
|
import { RollupError, renderRollupText, rollupFiles } from "./rollup.js";
|
|
36
|
+
import { buildRenovatePreset } from "./renovate-preset.js";
|
|
35
37
|
import { diagnoseEnvironment, renderDoctorText } from "./doctor.js";
|
|
36
38
|
import { createIssueReport, ReportIssueError } from "./report-issue.js";
|
|
37
|
-
import { renderCheckText, renderCodeQuality, renderCompact, renderFindingText, renderHtml, renderJson, renderMigrationPacketText, renderRdjson, renderSarif, renderText } from "./reporters.js";
|
|
39
|
+
import { renderCheckText, renderCodeQuality, renderCompact, renderFindingText, renderHtml, renderJson, renderJunit, renderMarkdown, renderMigrationPacketText, renderRdjson, renderSarif, renderSlack, renderTeams, renderText, renderWebhook } from "./reporters.js";
|
|
38
40
|
import { STATUS_FILE_NAME, writeStatusFile } from "./status-file.js";
|
|
39
41
|
import { browseFindings, canBrowse } from "./tui.js";
|
|
40
42
|
import { VERSION } from "./version.js";
|
|
43
|
+
import { exportActiveCle } from "./export/cle.js";
|
|
44
|
+
import { CycloneDxError, enrichCycloneDxFile } from "./sbom/cyclonedx.js";
|
|
41
45
|
import { addWatched, listWatched, removeWatched, renderWatchText, runWatch } from "./watch.js";
|
|
42
46
|
const HELP = `Evergreen — local-first deprecation intelligence
|
|
43
47
|
|
|
44
48
|
Usage:
|
|
45
|
-
evergreen scan [path] [--json | --format <text|json|sarif|rdjson|codequality|compact|html>]
|
|
49
|
+
evergreen scan [path] [--json | --format <text|json|markdown|junit|sarif|rdjson|codequality|compact|html|slack|teams|webhook>]
|
|
46
50
|
evergreen scan [path] --baseline <file> [--baseline-warn-days <n>] [--on-expired resurface|fail] [--today <YYYY-MM-DD>] [--json]
|
|
47
51
|
evergreen scan [path] --as-of <YYYY-MM-DD> [--json] # scan as the world will look on a date; --today is an alias
|
|
48
52
|
evergreen scan [path] --min-support-days <n> # warn on lines whose support ends within n days
|
|
@@ -54,13 +58,17 @@ Usage:
|
|
|
54
58
|
evergreen diff <base>..<head> [path] [--json] # findings a change introduced or fixed; exit 1 only when it introduced some
|
|
55
59
|
evergreen scan [path] --by-owner [--json] # group findings by CODEOWNERS owner; files with no owner are listed as unowned
|
|
56
60
|
evergreen rollup <report.json>... [--owners <file>] [--json] # merge saved scan reports by product and owner; reads files only
|
|
61
|
+
evergreen renovate-preset [path] # print Renovate packageRules for replacements and runtime pins; does not edit config
|
|
57
62
|
evergreen fix [path] [--dry-run] # print patches for deterministic fixes; nothing is written
|
|
58
63
|
evergreen fix [path] --branch # apply the fixes on a local evergreen/fix branch and commit; never pushes
|
|
59
64
|
evergreen baseline [path] [--output <file>]
|
|
60
65
|
evergreen check <purl> [--fail-on <severity|none>] [--json]
|
|
66
|
+
evergreen guard [--fail-on <severity|none>] [--force] npm install <package>... # check first; flags after npm go to npm
|
|
61
67
|
evergreen usage [--file <path>] [--json]
|
|
62
68
|
evergreen mcp
|
|
63
69
|
evergreen data status [--json]
|
|
70
|
+
evergreen export --format cle [--data-version <version>] # CLE 1.0 snapshots, one JSON document per line and component; skipped rules reported on stderr
|
|
71
|
+
evergreen sbom enrich <bom.json> [--data-version <version>] # print enriched CycloneDX JSON; leaves the input unchanged
|
|
64
72
|
evergreen watch add|remove <path> # register a project for the digest
|
|
65
73
|
evergreen watch [run] [--weekly] [--json] # re-scan registered projects, print only what changed
|
|
66
74
|
evergreen watch list [--json]
|
|
@@ -360,7 +368,7 @@ async function main(args) {
|
|
|
360
368
|
return 0;
|
|
361
369
|
}
|
|
362
370
|
const [command] = args;
|
|
363
|
-
if (command !== "scan" && command !== "check" && command !== "data" && command !== "new-detector" && command !== "init" && command !== "setup" && command !== "usage" && command !== "mcp" && command !== "explain" && command !== "context" && command !== "baseline" && command !== "forecast" && command !== "diff" && command !== "rollup" && command !== "fix" && command !== "doctor" && command !== "report-issue" && command !== "capabilities" && command !== "watch" && command !== "completions") {
|
|
371
|
+
if (command !== "sbom" && command !== "export" && command !== "scan" && command !== "check" && command !== "guard" && command !== "data" && command !== "new-detector" && command !== "init" && command !== "setup" && command !== "usage" && command !== "mcp" && command !== "explain" && command !== "context" && command !== "baseline" && command !== "forecast" && command !== "diff" && command !== "rollup" && command !== "renovate-preset" && command !== "fix" && command !== "doctor" && command !== "report-issue" && command !== "capabilities" && command !== "watch" && command !== "completions") {
|
|
364
372
|
return fail("usage", `Unknown command: ${command ?? ""}`, `\n${HELP}`);
|
|
365
373
|
}
|
|
366
374
|
if (command === "mcp") {
|
|
@@ -402,19 +410,60 @@ async function main(args) {
|
|
|
402
410
|
}
|
|
403
411
|
const formatIndex = args.indexOf("--format");
|
|
404
412
|
const format = formatIndex === -1 ? undefined : args[formatIndex + 1];
|
|
405
|
-
if (
|
|
406
|
-
|
|
413
|
+
if (command === "sbom") {
|
|
414
|
+
const operands = args.slice(1).filter((_, index) => dataVersionIndex === -1 ||
|
|
415
|
+
(index + 1 !== dataVersionIndex && index + 1 !== dataVersionIndex + 1));
|
|
416
|
+
if (operands.length !== 2 || operands[0] !== "enrich" || operands[1].startsWith("-")) {
|
|
417
|
+
return fail("usage", "Usage: evergreen sbom enrich <bom.json> [--data-version <version>]");
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
const bom = await enrichCycloneDxFile(operands[1]);
|
|
421
|
+
process.stdout.write(`${JSON.stringify(bom, null, 2)}\n`);
|
|
422
|
+
return 0;
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
if (error instanceof CycloneDxError)
|
|
426
|
+
return fail("parse", error.message);
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (command === "export") {
|
|
431
|
+
const operands = args.slice(1).filter((_, index) => index + 1 !== formatIndex && index + 1 !== formatIndex + 1 &&
|
|
432
|
+
(dataVersionIndex === -1 || (index + 1 !== dataVersionIndex && index + 1 !== dataVersionIndex + 1)));
|
|
433
|
+
if (format !== "cle" || operands.length > 0) {
|
|
434
|
+
return fail("usage", "Usage: evergreen export --format cle [--data-version <version>]");
|
|
435
|
+
}
|
|
436
|
+
const result = await exportActiveCle();
|
|
437
|
+
for (const document of result.documents)
|
|
438
|
+
process.stdout.write(`${JSON.stringify(document)}\n`);
|
|
439
|
+
if (result.skipped > 0)
|
|
440
|
+
process.stderr.write(`Skipped ${result.skipped} rules: CLE requires a dated component lifecycle event; deprecation, removal, symbol rules, and undated rules cannot be faithfully represented.\n`);
|
|
441
|
+
return 0;
|
|
442
|
+
}
|
|
443
|
+
if (formatIndex !== -1 && format !== "text" && format !== "json" && format !== "markdown" && format !== "junit" && format !== "sarif" && format !== "rdjson" && format !== "codequality" && format !== "compact" && format !== "html" && format !== "slack" && format !== "teams" && format !== "webhook") {
|
|
444
|
+
return fail("usage", `Unknown format: ${format ?? ""} (expected text, json, markdown, junit, sarif, rdjson, codequality, compact, html, slack, teams, or webhook).`);
|
|
407
445
|
}
|
|
408
446
|
if (command === "check" && format !== undefined && format !== "json" && format !== "text") {
|
|
409
447
|
return fail("usage", "check supports --json and --format text|json only.");
|
|
410
448
|
}
|
|
411
|
-
if ((format === "sarif" || format === "rdjson" || format === "codequality" || format === "compact" || format === "html") && command !== "scan") {
|
|
449
|
+
if ((format === "markdown" || format === "junit" || format === "sarif" || format === "rdjson" || format === "codequality" || format === "compact" || format === "html" || format === "slack" || format === "teams" || format === "webhook") && command !== "scan") {
|
|
412
450
|
return fail("usage", `--format ${format} is only available for the scan command.`);
|
|
413
451
|
}
|
|
414
452
|
const json = args.includes("--json") || format === "json";
|
|
415
453
|
if (args.includes("--write-status") && command !== "scan") {
|
|
416
454
|
return fail("usage", "--write-status is only available for the scan command.");
|
|
417
455
|
}
|
|
456
|
+
if (command === "guard") {
|
|
457
|
+
try {
|
|
458
|
+
const result = await guardCommand(args.slice(1), { report: (output) => process.stderr.write(`${output}\n`) });
|
|
459
|
+
return result.exitCode;
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
if (error instanceof GuardUsageError)
|
|
463
|
+
return fail("usage", error.message);
|
|
464
|
+
throw error;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
418
467
|
if (command === "watch")
|
|
419
468
|
return watch(args.slice(1), json);
|
|
420
469
|
if (command === "capabilities") {
|
|
@@ -513,6 +562,14 @@ async function main(args) {
|
|
|
513
562
|
throw error;
|
|
514
563
|
}
|
|
515
564
|
}
|
|
565
|
+
if (command === "renovate-preset") {
|
|
566
|
+
if (positional.length > 1)
|
|
567
|
+
return fail("usage", "Usage: evergreen renovate-preset [path]");
|
|
568
|
+
const result = await scanRepository(resolve(pathArgument), builtInDetectors);
|
|
569
|
+
const preset = buildRenovatePreset(result.findings, await bundledRules());
|
|
570
|
+
process.stdout.write(`${JSON.stringify(preset, null, 2)}\n`);
|
|
571
|
+
return 0;
|
|
572
|
+
}
|
|
516
573
|
if (command === "diff") {
|
|
517
574
|
const range = positional[0];
|
|
518
575
|
if (!range)
|
|
@@ -693,7 +750,7 @@ async function main(args) {
|
|
|
693
750
|
process.stdout.write(`${json ? JSON.stringify(groups, null, 2) : renderOwnerGroupsText(groups)}\n`);
|
|
694
751
|
}
|
|
695
752
|
else {
|
|
696
|
-
const output = format === "sarif" ? renderSarif(result) : format === "rdjson" ? renderRdjson(result) : format === "codequality" ? renderCodeQuality(result) : format === "compact" ? renderCompact(result) : format === "html" ? renderHtml(result) : json ? renderJson(result) : renderText(result, { firstRun: command === "scan" && !(await hasPersonalConfig()) });
|
|
753
|
+
const output = format === "sarif" ? renderSarif(result) : format === "rdjson" ? renderRdjson(result) : format === "codequality" ? renderCodeQuality(result) : format === "compact" ? renderCompact(result) : format === "html" ? renderHtml(result) : format === "markdown" ? renderMarkdown(result) : format === "junit" ? renderJunit(result) : format === "slack" ? renderSlack(result) : format === "teams" ? renderTeams(result) : format === "webhook" ? renderWebhook(result) : json ? renderJson(result) : renderText(result, { firstRun: command === "scan" && !(await hasPersonalConfig()) });
|
|
697
754
|
process.stdout.write(`${output}\n`);
|
|
698
755
|
if (args.includes("--write-status")) {
|
|
699
756
|
const statusRoot = resolve(pathArgument);
|
package/dist/src/completions.js
CHANGED
|
@@ -6,8 +6,10 @@ export const COMMANDS = [
|
|
|
6
6
|
{ name: "forecast", flags: ["--months", "--as-of", "--today", "--json", "--data-version"], example: "evergreen forecast . --months 6" },
|
|
7
7
|
{ name: "diff", flags: ["--json", "--data-version"], example: "evergreen diff main..HEAD" },
|
|
8
8
|
{ name: "rollup", flags: ["--owners", "--json"], example: "evergreen rollup reports/*.json --owners owners.json" },
|
|
9
|
+
{ name: "renovate-preset", flags: ["--data-version"], example: "evergreen renovate-preset ." },
|
|
9
10
|
{ name: "fix", flags: ["--dry-run", "--data-version"], example: "evergreen fix . --dry-run" },
|
|
10
11
|
{ name: "check", flags: ["--fail-on", "--json", "--format", "--data-version"], example: "evergreen check pkg:npm/request@2.88.2" },
|
|
12
|
+
{ name: "guard", flags: ["--fail-on", "--force", "--data-version"], example: "evergreen guard npm install request" },
|
|
11
13
|
{ name: "usage", flags: ["--file", "--json"], example: "evergreen usage --json" },
|
|
12
14
|
{ name: "mcp", flags: [], example: "evergreen mcp" },
|
|
13
15
|
{ name: "data", flags: ["--json", "--data-version"], example: "evergreen data status" },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const DATA_BUNDLE_SCHEMA_VERSION: "evergreen.data-bundle.v1";
|
|
2
2
|
/** The registries a bundle carries, in the order they are listed. The SHA map is carried as kind "sha-map". */
|
|
3
|
-
export declare const BUNDLED_ECOSYSTEMS: readonly ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "python", "ruby", "rust", "serverless", "shell", "swift", "terraform"];
|
|
3
|
+
export declare const BUNDLED_ECOSYSTEMS: readonly ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "osv", "python", "ruby", "rust", "serverless", "shell", "swift", "terraform"];
|
|
4
4
|
/**
|
|
5
5
|
* Who may sign a data bundle: the keyless Sigstore certificate must carry this OIDC issuer and an identity
|
|
6
6
|
* (the signing workflow's ref) that matches the pattern. The pattern names the repository, the workflow file, and
|
package/dist/src/data-bundle.js
CHANGED
|
@@ -4,7 +4,7 @@ import { resolve } from "node:path";
|
|
|
4
4
|
import { DEFAULT_MAX_REVIEW_AGE_DAYS, validateRegistryV2 } from "./registry.js";
|
|
5
5
|
export const DATA_BUNDLE_SCHEMA_VERSION = "evergreen.data-bundle.v1";
|
|
6
6
|
/** The registries a bundle carries, in the order they are listed. The SHA map is carried as kind "sha-map". */
|
|
7
|
-
export const BUNDLED_ECOSYSTEMS = ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "python", "ruby", "rust", "serverless", "shell", "swift", "terraform"];
|
|
7
|
+
export const BUNDLED_ECOSYSTEMS = ["android", "ci-images", "container", "cpp", "dart", "database", "dotnet", "github-actions", "go", "java", "kubernetes", "nodejs", "npm", "osv", "python", "ruby", "rust", "serverless", "shell", "swift", "terraform"];
|
|
8
8
|
const SHA_MAP_PATH = "registry/github-actions-shas.json";
|
|
9
9
|
/**
|
|
10
10
|
* Who may sign a data bundle: the keyless Sigstore certificate must carry this OIDC issuer and an identity
|