Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .linelint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ rules:
ignore:
- extractor/filesystem/os/dpkg/testdata/dpkg/trailingnewlines
- extractor/filesystem/os/dpkg/testdata/opkg/trailingnewlines
- extractor/filesystem/language/julia/manifesttoml/testdata/no-dependency.toml
- extractor/filesystem/language/julia/projecttoml/testdata/not-toml.txt
- extractor/filesystem/language/julia/manifesttoml/testdata/no-dependency.toml
- extractor/filesystem/language/julia/projecttoml/testdata/project.toml
88 changes: 45 additions & 43 deletions docs/supported_inventory_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,49 +57,51 @@ See the docs on [how to add a new Extractor](/docs/new_extractor.md).

### Language packages

| Language | Details | Extractor Plugin(s) |
|------------|-------------------------------------------|--------------------------------------|
| .NET | packages.lock.json | `dotnet/packageslockjson` |
| | packages.config | `dotnet/packagesconfig` |
| | deps.json | `dotnet/depsjson` |
| | portable executables | `dotnet/pe` |
| C++ | Conan packages | `cpp/conanlock` |
| Dart | pubspec.lock | `dart/pubspec` |
| Erlang | mix.lock | `erlang/mixlock` |
| Elixir | mix.lock | `elixir/mixlock` |
| Go | Go binaries | `go/binary` |
| | go.mod (OSV) | `go/gomod` |
| Haskell | stack.yaml.lock | `haskell/stacklock` |
| | cabal.project.freeze | `haskell/cabal` |
| Java | Java archives | `java/archive` |
| | pom.xml | `java/pomxml`, `java/pomxmlnet` |
| | gradle.lockfile | `java/gradlelockfile` |
| | verification-metadata.xml | `java/gradleverificationmetadataxml` |
| Javascript | Installed NPM packages (package.json) | `javascript/packagejson` |
| | package-lock.json, npm-shrinkwrap.json | `javascript/packagelockjson` |
| | yarn.lock | `javascript/yarnlock` |
| | pnpm-lock.yaml | `javascript/pnpmlock` |
| | bun.lock | `javascript/bunlock` |
| Lua | Luarocks modules | `lua/luarocks` |
| ObjectiveC | Podfile.lock | `swift/podfilelock` |
| PHP | Composer | `php/composerlock` |
| Python | Installed PyPI packages (global and venv) | `python/wheelegg` |
| | requirements.txt | `python/requirements` |
| | poetry.lock | `python/poetrylock` |
| | Pipfile.lock | `python/pipfilelock` |
| | pdm.lock | `python/pdmlock` |
| | Conda packages | `python/condameta` |
| | setup.py | `python/setup` |
| | uv.lock | `python/uvlock` |
| R | renv.lock | `r/renvlock` |
| Ruby | Installed Gem packages | `ruby/gemspec` |
| | Gemfile.lock (OSV) | `ruby/gemfilelock` |
| Rust | Cargo.lock | `rust/cargolock` |
| | Cargo.toml | `rust/cargotoml` |
| | Rust binaries | `rust/cargoauditable` |
| Swift | Podfile.lock | `swift/podfilelock` |
| | Package.resolved | `swift/packageresolved` |
| Nim | Nimble packages | `nim/nimble` |
| Language | Details | Extractor Plugin(s) |
|------------|---------------------------------------------------|--------------------------------------|
| .NET | packages.lock.json | `dotnet/packageslockjson` |
| | packages.config | `dotnet/packagesconfig` |
| | deps.json | `dotnet/depsjson` |
| | portable executables | `dotnet/pe` |
| C++ | Conan packages | `cpp/conanlock` |
| Dart | pubspec.lock | `dart/pubspec` |
| Erlang | mix.lock | `erlang/mixlock` |
| Elixir | mix.lock | `elixir/mixlock` |
| Go | Go binaries | `go/binary` |
| | go.mod (OSV) | `go/gomod` |
| Haskell | stack.yaml.lock | `haskell/stacklock` |
| | cabal.project.freeze | `haskell/cabal` |
| Java | Java archives | `java/archive` |
| | pom.xml | `java/pomxml`, `java/pomxmlnet` |
| | gradle.lockfile | `java/gradlelockfile` |
| | verification-metadata.xml | `java/gradleverificationmetadataxml` |
| Javascript | Installed NPM packages (package.json) | `javascript/packagejson` |
| | package-lock.json, npm-shrinkwrap.json | `javascript/packagelockjson` |
| | yarn.lock | `javascript/yarnlock` |
| | pnpm-lock.yaml | `javascript/pnpmlock` |
| | bun.lock | `javascript/bunlock` |
| Julia | Julia package/project dependencies (Project.toml) | `julia/projecttoml` |
| | Julia installed packages (Manifest.toml) | `julia/manifesttoml` |
| Lua | Luarocks modules | `lua/luarocks` |
| ObjectiveC | Podfile.lock | `swift/podfilelock` |
| PHP | Composer | `php/composerlock` |
| Python | Installed PyPI packages (global and venv) | `python/wheelegg` |
| | requirements.txt | `python/requirements` |
| | poetry.lock | `python/poetrylock` |
| | Pipfile.lock | `python/pipfilelock` |
| | pdm.lock | `python/pdmlock` |
| | Conda packages | `python/condameta` |
| | setup.py | `python/setup` |
| | uv.lock | `python/uvlock` |
| R | renv.lock | `r/renvlock` |
| Ruby | Installed Gem packages | `ruby/gemspec` |
| | Gemfile.lock (OSV) | `ruby/gemfilelock` |
| Rust | Cargo.lock | `rust/cargolock` |
| | Cargo.toml | `rust/cargotoml` |
| | Rust binaries | `rust/cargoauditable` |
| Swift | Podfile.lock | `swift/podfilelock` |
| | Package.resolved | `swift/packageresolved` |
| Nim | Nimble packages | `nim/nimble` |

### Language runtime managers
| runtime | Details | Extractor Plugin(s) |
Expand Down
131 changes: 131 additions & 0 deletions extractor/filesystem/language/julia/manifesttoml/manifesttoml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package manifesttoml extracts Manifest.toml files for Julia projects
package manifesttoml

import (
"context"
"fmt"
"path/filepath"

"github.com/BurntSushi/toml"

"github.com/google/osv-scalibr/extractor"
"github.com/google/osv-scalibr/extractor/filesystem"
"github.com/google/osv-scalibr/inventory"
"github.com/google/osv-scalibr/plugin"
"github.com/google/osv-scalibr/purl"
)

const (
// Name is the name of the Extractor.
Name = "julia/manifesttoml"
)

type juliaManifestDependency struct {
Version string `toml:"version"`
GitTreeSha1 string `toml:"git-tree-sha1"`
RepoURL string `toml:"repo-url"`
Deps []string `toml:"deps"`
}

type juliaManifestFile struct {
ManifestFormat string `toml:"manifest_format"`
Dependencies map[string][]juliaManifestDependency `toml:"deps"`
}

// Extractor extracts Julia packages from Manifest.toml files.
type Extractor struct{}

// New returns a new instance of the extractor.
func New() filesystem.Extractor { return &Extractor{} }

// Name of the extractor
func (e Extractor) Name() string { return Name }

// Version of the extractor
func (e Extractor) Version() int { return 0 }

// FileRequired returns true if the specified file matches Julia Manifest.toml file patterns.
func (e Extractor) FileRequired(api filesystem.FileAPI) bool {
return filepath.Base(api.Path()) == "Manifest.toml"
}

// Requirements of the extractor
func (e Extractor) Requirements() *plugin.Capabilities {
return &plugin.Capabilities{}
}

// Extract extracts packages from Julia Manifest.toml files passed through the scan input.
func (e Extractor) Extract(ctx context.Context, input *filesystem.ScanInput) (inventory.Inventory, error) {
var parsedTomlFile juliaManifestFile

_, err := toml.NewDecoder(input.Reader).Decode(&parsedTomlFile)
if err != nil {
return inventory.Inventory{}, fmt.Errorf("could not extract: %w", err)
}

packages := make([]*extractor.Package, 0, len(parsedTomlFile.Dependencies))

for name, dependencies := range parsedTomlFile.Dependencies {
if err := ctx.Err(); err != nil {
return inventory.Inventory{Packages: packages}, fmt.Errorf("%s halted due to context error: %w", e.Name(), err)
}

// Take the first dependency entry (Julia typically has one entry per package name)
if len(dependencies) == 0 {
continue
}
dependency := dependencies[0]
// Skip dependencies that have no version
if dependency.Version == "" {
continue
}

var srcCode *extractor.SourceCodeIdentifier
if dependency.GitTreeSha1 != "" {
srcCode = &extractor.SourceCodeIdentifier{
Commit: dependency.GitTreeSha1,
Repo: dependency.RepoURL, // Include repo-url if available
}
}

packages = append(packages, &extractor.Package{
Name: name,
Version: dependency.Version,
PURLType: purl.TypeJulia,
Locations: []string{input.Path},
SourceCode: srcCode,
})
}

return inventory.Inventory{Packages: packages}, nil
}

var _ filesystem.Extractor = Extractor{}
Loading