Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ local-build-commands.txt
*.log

.DS_Store

!**/rulesets/**/*.tar.gz
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@
### Added
- Upcoming changes...

## [0.9.0] - 2025-12-29
### Added
- Added gRPC DownloadRuleset and REST endpoint GET /v2/cryptography/rulesets/download
- Supports downloading cryptography detection rulesets by name and version
- Supports "latest" keyword for retrieving the most recent version
- Returns tarball with appropriate HTTP headers (Content-Disposition, SCANOSS-Ruleset-Name, SCANOSS-Ruleset-Version, X-Checksum-SHA256)
- Includes metadata validation and version resolution via symlinks

### Fixed
- Fixed OpenTelemetry metrics initialization by properly exporting SetupMetrics function

## [0.8.1] - 2025-10-16
### Fixed
- Fixed OpenTelemetry metrics initialization by exporting SetupMetrics function and calling it on server startup
Expand Down Expand Up @@ -96,6 +107,7 @@
- Remove from list those versions that do not contain detections
- Detailed response status message.

[0.9.0]: https://github.com/scanoss/cryptography/compare/v0.8.1...v0.9.0
[0.8.1]: https://github.com/scanoss/cryptography/compare/v0.8.0...v0.8.1
[0.8.0]: https://github.com/scanoss/cryptography/compare/v0.7.1...v0.8.0
[0.7.1]: https://github.com/scanoss/cryptography/compare/v0.7.0...v0.7.1
Expand All @@ -104,4 +116,4 @@
[0.5.0]: https://github.com/scanoss/cryptography/compare/v0.4.2...v0.5.0
[0.4.2]: https://github.com/scanoss/cryptography/releases/tag/v0.4.1....v0.4.2
[0.4.1]: https://github.com/scanoss/cryptography/releases/tag/v0.4.0...v0.4.1
[0.4.0]: https://github.com/scanoss/cryptography/releases/tag/v0.4.0
[0.4.0]: https://github.com/scanoss/cryptography/releases/tag/v0.4.0
5 changes: 4 additions & 1 deletion config/app-config-dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
"App": {
"Name": "Cryptography Service",
"Debug": true,
"Mode": "dev"
"Mode": "dev"
},
"Database": {
"Dsn": "./test-support/sqlite/scanoss.db?cache=shared&mode=memory",
"Driver": "sqlite",
"Trace": true
},
"Rulesets": {
"StoragePath": "test-support/rulesets"
}
}
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/lib/pq v1.10.9
github.com/scanoss/go-grpc-helper v0.9.0
github.com/scanoss/go-purl-helper v0.2.1
github.com/scanoss/papi v0.25.1
github.com/scanoss/papi v0.27.0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n go.mod | head -50

Repository: scanoss/cryptography

Length of output: 2465


🌐 Web query:

github.com/scanoss/papi v0.27.0 security vulnerabilities

💡 Result:

I can look that up — do you mean the GitHub repo github.com/scanoss/papi (the SCANOSS project) at tag/version v0.27.0, or a different package named “papi” (there are many projects named papi)? Please confirm which exact repository/package and I'll search for known security vulnerabilities for that version.


🌐 Web query:

google.golang.org/genproto security vulnerabilities CVE

💡 Result:

I found no CVE entries that name google.golang.org/genproto itself. However there are public CVEs in closely-related Go protobuf packages that commonly appear in genproto dependency graphs:

  • CVE-2023-24535 / GO-2023-1631 — parsing invalid text-format protobuf messages can cause a panic (DoS). Affects google.golang.org/protobuf (fixed in v1.29.1). [1][2]
  • CVE-2024-24786 — infinite loop in protojson.Unmarshal for certain invalid JSON, affecting google.golang.org/protobuf < 1.33.0 (fix in v1.33.0). [3][4]

If you use google.golang.org/genproto, check your dependency tree for google.golang.org/protobuf (and google.golang.org/grpc) versions and upgrade those transitive deps to the fixed releases. For details and patches see the cited advisories.

Sources: [1] go.dev vuln GO-2023-1631 (CVE-2023-24535). [2] Snyk / advisories on CVE-2023-24535. [3] Snyk/Seal/Resolved listings for CVE-2024-24786. [4] additional vulnerability pages summarizing CVE-2024-24786.


🏁 Script executed:

# Check if these packages exist in Go registry by examining their module info
# We can use a simple approach to verify package existence
echo "=== Checking papi v0.27.0 availability ==="
curl -s -I "https://pkg.go.dev/github.com/scanoss/[email protected]" | head -5

echo -e "\n=== Checking genproto version ==="
curl -s -I "https://pkg.go.dev/google.golang.org/[email protected]" | head -5

Repository: scanoss/cryptography

Length of output: 1676


🏁 Script executed:

cat -n go.mod | tail -50

Repository: scanoss/cryptography

Length of output: 2854


🏁 Script executed:

# Check for protobuf and grpc versions in go.mod
rg "google.golang.org/(protobuf|grpc)" go.mod

Repository: scanoss/cryptography

Length of output: 240


🌐 Web query:

SCANOSS papi github releases v0.27.0

💡 Result:

I couldn't find a GitHub release or tag named "v0.27.0" for scanoss/papi. The scanoss GitHub organization and the papi repository pages show the project but no v0.27.0 release; libraries.io lists papi at around v0.17.0. [1][2]

Sources:

  • SCANOSS GitHub (organization / repos). [1]
  • Libraries.io page for github.com/scanoss/papi (shows v0.17.0). [2]

🏁 Script executed:

# Check if we can find more details about the dependencies
# Let's verify the genproto package requirements more closely
echo "=== Genproto version analysis ==="
echo "Pseudo-version: v0.0.0-20250707201910-8d1bb00bc6a7"
echo "This is a development snapshot, not a release version"
echo "Format: v0.0.0-YYYYMMDDHHMMSS-githash"
echo ""
echo "Note: The date 2025-07-07 is in the future (current: Dec 2025)"
echo "This suggests the version is from July 7, 2025"

Repository: scanoss/cryptography

Length of output: 351


🏁 Script executed:

# Check GitHub for SCANOSS papi package information
# Search for papi releases and any security information
curl -s "https://api.github.com/repos/scanoss/papi/releases/tags/v0.27.0" | head -20

Repository: scanoss/cryptography

Length of output: 1213


The genproto dependency uses a pseudo-version instead of a stable release.

papi v0.27.0 exists and is a valid published release with no known security vulnerabilities. However, google.golang.org/genproto/googleapis/api is pinned to a pseudo-version (v0.0.0-20250707201910-8d1bb00bc6a7) rather than a stable release. While this specific commit snapshot appears to be from July 2025 and the underlying transitive dependencies (protobuf v1.36.6, grpc v1.75.0) include patches for known CVEs, using a pseudo-version for a direct dependency is unusual and should be replaced with a proper release version to improve reproducibility and maintainability.

github.com/scanoss/zap-logging-helper v0.4.0
github.com/stretchr/testify v1.11.1
go.opentelemetry.io/otel v1.38.0
Expand All @@ -37,7 +37,7 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
)

Expand Down Expand Up @@ -70,6 +70,6 @@ require (
// Details of how to use the "replace" command for local development
// https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive
// ie. replace github.com/scanoss/papi => ../papi
// replace github.com/scanoss/papi => ../papi
//replace github.com/scanoss/papi => ../papi

// require github.com/scanoss/papi v0.0.0-unpublished
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -626,8 +626,8 @@ github.com/scanoss/go-purl-helper v0.2.1 h1:jp960a585ycyJSlqZky1NatMJBIQi/JGITDf
github.com/scanoss/go-purl-helper v0.2.1/go.mod h1:v20/bKD8G+vGrILdiq6r0hyRD2bO8frCJlu9drEcQ38=
github.com/scanoss/ipfilter/v2 v2.0.2 h1:GaB9i8kVJg9JQZm5XGStYkEpiaCVdsrj7ezI2wV/oh8=
github.com/scanoss/ipfilter/v2 v2.0.2/go.mod h1:AwrpX4XGbZ7EKISMi1d6E5csBk1nWB8+ugpvXHFcTpA=
github.com/scanoss/papi v0.25.1 h1:/OUoCWkrD+PRNvssSfrVcgPFFHKl3rCp/zjz0I2qNd8=
github.com/scanoss/papi v0.25.1/go.mod h1:Z4E/4IpwYdzHHRJXTgBCGG1GjksgrFjNW5cvhbKUfeU=
github.com/scanoss/papi v0.27.0 h1:raPAm9aFmcGVHdYZh+7su20R0ow+9tpI3jMDxxQaFUM=
github.com/scanoss/papi v0.27.0/go.mod h1:Z4E/4IpwYdzHHRJXTgBCGG1GjksgrFjNW5cvhbKUfeU=
github.com/scanoss/zap-logging-helper v0.4.0 h1:2qTYoaFa9+MlD2/1wmPtiDHfh+42NIEwgKVU3rPpl0Y=
github.com/scanoss/zap-logging-helper v0.4.0/go.mod h1:9QuEZcq73g/0Izv1tWeOWukoIK0oTBzM4jSNQ5kRR1w=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
Expand Down
31 changes: 31 additions & 0 deletions pkg/config/server_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
package config

import (
"fmt"
"os"

"github.com/golobby/config/v3"
"github.com/golobby/config/v3/pkg/feeder"
)
Expand Down Expand Up @@ -68,6 +71,9 @@ type ServerConfig struct {
BlockByDefault bool `env:"CRYPTO_BLOCK_BY_DEFAULT"` // Block request by default if they are not in the allow list
TrustProxy bool `env:"CRYPTO_TRUST_PROXY"` // Trust the interim proxy or not (causes the source IP to be validated instead of the proxy)
}
Rulesets struct {
StoragePath string `env:"RULESETS_STORAGE_PATH"` // Path to directory containing cryptography detection rulesets
}
}

// NewServerConfig loads all config options and return a struct for use.
Expand All @@ -84,6 +90,9 @@ func NewServerConfig(feeders []config.Feeder) (*ServerConfig, error) {
if err != nil {
return nil, err
}
if err := cfg.ValidateRulesetsFolder(); err != nil {
return nil, err
}
return &cfg, nil
}

Expand All @@ -105,4 +114,26 @@ func setServerConfigDefaults(cfg *ServerConfig) {
cfg.Logging.DynamicPort = "localhost:60054"
cfg.Telemetry.Enabled = false
cfg.Telemetry.OltpExporter = "0.0.0.0:4317" // Default OTEL OLTP gRPC Exporter endpoint
cfg.Rulesets.StoragePath = "/var/lib/scanoss/cryptography/rulesets"
}

// ValidateRulesetsFolder validates that the configured rulesets storage path exists and is a directory.
func (cfg *ServerConfig) ValidateRulesetsFolder() error {
if cfg.Rulesets.StoragePath == "" {
return fmt.Errorf("rulesets storage path is not configured")
}

info, err := os.Stat(cfg.Rulesets.StoragePath)
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("rulesets storage path does not exist: %s", cfg.Rulesets.StoragePath)
}
return fmt.Errorf("failed to access rulesets storage path: %w", err)
}

if !info.IsDir() {
return fmt.Errorf("rulesets storage path is not a directory: %s", cfg.Rulesets.StoragePath)
}

return nil
}
82 changes: 78 additions & 4 deletions pkg/config/server_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,19 @@ import (

"github.com/golobby/config/v3"
"github.com/golobby/config/v3/pkg/feeder"
"scanoss.com/cryptography/pkg/testutils"
)

func TestServerConfig(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

dbUser := "test-user"
err := os.Setenv("DB_USER", dbUser)
if err != nil {
t.Fatalf("an error '%s' was not expected when creating new config instance", err)
}
defer os.Unsetenv("DB_USER")

cfg, err := NewServerConfig(nil)
if err != nil {
t.Fatalf("an error '%s' was not expected when creating new config instance", err)
Expand All @@ -39,17 +44,16 @@ func TestServerConfig(t *testing.T) {
t.Errorf("DB user '%v' doesn't match expected: %v", cfg.Database.User, dbUser)
}
fmt.Printf("Server Config1: %+v\n", cfg)
err = os.Unsetenv("DB_USER")
if err != nil {
fmt.Printf("Warning: Problem runn Unsetenv: %v\n", err)
}
}

func TestServerConfigDotEnv(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

err := os.Unsetenv("DB_USER")
if err != nil {
fmt.Printf("Warning: Problem runn Unsetenv: %v\n", err)
}

dbUser := "env-user"
var feeders []config.Feeder
feeders = append(feeders, feeder.DotEnv{Path: "tests/dot-env"})
Expand All @@ -64,10 +68,13 @@ func TestServerConfigDotEnv(t *testing.T) {
}

func TestServerConfigJson(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

err := os.Unsetenv("DB_USER")
if err != nil {
fmt.Printf("Warning: Problem runn Unsetenv: %v\n", err)
}

dbUser := "json-user"
var feeders []config.Feeder
feeders = append(feeders, feeder.Json{Path: "tests/env.json"})
Expand All @@ -80,3 +87,70 @@ func TestServerConfigJson(t *testing.T) {
}
fmt.Printf("Server Config3: %+v\n", cfg)
}

func TestValidateRulesetsFolder(t *testing.T) {
// Test with non-existent path
t.Run("NonExistentPath", func(t *testing.T) {
err := os.Setenv("RULESETS_STORAGE_PATH", "/path/that/does/not/exist")
if err != nil {
t.Fatalf("failed to set RULESETS_STORAGE_PATH: %s", err)
}
defer os.Unsetenv("RULESETS_STORAGE_PATH")

_, err = NewServerConfig(nil)
if err == nil {
t.Fatal("expected error for non-existent rulesets path, got nil")
}
if !os.IsNotExist(err) && err.Error() != "rulesets storage path does not exist: /path/that/does/not/exist" {
t.Errorf("unexpected error message: %v", err)
}
})

// Test with valid directory
t.Run("ValidDirectory", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "rulesets-test-*")
if err != nil {
t.Fatalf("failed to create temp directory: %s", err)
}
defer os.RemoveAll(tmpDir)

err = os.Setenv("RULESETS_STORAGE_PATH", tmpDir)
if err != nil {
t.Fatalf("failed to set RULESETS_STORAGE_PATH: %s", err)
}
defer os.Unsetenv("RULESETS_STORAGE_PATH")

cfg, err := NewServerConfig(nil)
if err != nil {
t.Fatalf("unexpected error for valid rulesets path: %s", err)
}
if cfg.Rulesets.StoragePath != tmpDir {
t.Errorf("expected storage path %s, got %s", tmpDir, cfg.Rulesets.StoragePath)
}
})

// Test with file instead of directory
t.Run("FileInsteadOfDirectory", func(t *testing.T) {
tmpFile, err := os.CreateTemp("", "rulesets-file-*")
if err != nil {
t.Fatalf("failed to create temp file: %s", err)
}
tmpFile.Close()
defer os.Remove(tmpFile.Name())

err = os.Setenv("RULESETS_STORAGE_PATH", tmpFile.Name())
if err != nil {
t.Fatalf("failed to set RULESETS_STORAGE_PATH: %s", err)
}
defer os.Unsetenv("RULESETS_STORAGE_PATH")

_, err = NewServerConfig(nil)
if err == nil {
t.Fatal("expected error for file instead of directory, got nil")
}
expectedMsg := fmt.Sprintf("rulesets storage path is not a directory: %s", tmpFile.Name())
if err.Error() != expectedMsg {
t.Errorf("expected error message '%s', got '%s'", expectedMsg, err.Error())
}
})
}
9 changes: 9 additions & 0 deletions pkg/handlers/algorithm_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ import (
_ "modernc.org/sqlite"
myconfig "scanoss.com/cryptography/pkg/config"
"scanoss.com/cryptography/pkg/models"
"scanoss.com/cryptography/pkg/testutils"
)

func TestNewCryptographyAlgorithmHandler(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

err := zlog.NewSugaredDevLogger()
if err != nil {
t.Fatalf("failed to initialize logger: %v", err)
Expand Down Expand Up @@ -57,6 +60,8 @@ func TestNewCryptographyAlgorithmHandler(t *testing.T) {
}

func TestCryptographyAlgorithmHandler_GetAlgorithms(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

ctx := context.Background()
err := zlog.NewSugaredDevLogger()
if err != nil {
Expand Down Expand Up @@ -176,6 +181,8 @@ func TestCryptographyAlgorithmHandler_GetAlgorithms(t *testing.T) {
}

func TestCryptographyAlgorithmHandler_GetComponentsAlgorithms(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

ctx := context.Background()
err := zlog.NewSugaredDevLogger()
if err != nil {
Expand Down Expand Up @@ -313,6 +320,8 @@ func TestCryptographyAlgorithmHandler_GetComponentsAlgorithms(t *testing.T) {
}

func TestCryptographyAlgorithmHandler_GetComponentAlgorithms(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

ctx := context.Background()
err := zlog.NewSugaredDevLogger()
if err != nil {
Expand Down
9 changes: 9 additions & 0 deletions pkg/handlers/algorithm_in_range_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ import (
_ "modernc.org/sqlite"
myconfig "scanoss.com/cryptography/pkg/config"
"scanoss.com/cryptography/pkg/models"
"scanoss.com/cryptography/pkg/testutils"
)

func TestNewAlgorithmInRangeHandler(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

err := zlog.NewSugaredDevLogger()
if err != nil {
t.Fatalf("failed to initialize logger: %v", err)
Expand Down Expand Up @@ -57,6 +60,8 @@ func TestNewAlgorithmInRangeHandler(t *testing.T) {
}

func TestAlgorithmInRangeHandler_GetAlgorithmsInRange(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

ctx := context.Background()
err := zlog.NewSugaredDevLogger()
if err != nil {
Expand Down Expand Up @@ -176,6 +181,8 @@ func TestAlgorithmInRangeHandler_GetAlgorithmsInRange(t *testing.T) {
}

func TestAlgorithmInRangeHandler_GetComponentsAlgorithmsInRange(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

ctx := context.Background()
err := zlog.NewSugaredDevLogger()
if err != nil {
Expand Down Expand Up @@ -313,6 +320,8 @@ func TestAlgorithmInRangeHandler_GetComponentsAlgorithmsInRange(t *testing.T) {
}

func TestAlgorithmInRangeHandler_GetComponentAlgorithmsInRange(t *testing.T) {
defer testutils.SetupTestRulesetsDir(t)()

ctx := context.Background()
err := zlog.NewSugaredDevLogger()
if err != nil {
Expand Down
7 changes: 5 additions & 2 deletions pkg/handlers/cryptography_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,21 @@ import (
// about handler performance and request processing times.
type metricsCounters struct {
cryptoAlgorithmsHistogram metric.Int64Histogram // Histogram for recording crypto algorithms request times in milliseconds
downloadRulesetHistogram metric.Int64Histogram // Histogram for recording ruleset download request times in milliseconds
downloadRulesetCounter metric.Int64Counter // Counter for tracking the number of downloaded rulesets
}

var oltpMetrics = metricsCounters{}

// setupMetrics configures all OpenTelemetry metric instruments for the handlers package.
// SetupMetrics configures all OpenTelemetry metric instruments for the handlers package.
//
// This function initializes histogram metrics for tracking request durations.
// It should be called once during handler initialization to set up the metrics infrastructure.

func SetupMetrics() {
meter := otel.Meter("scanoss.com/cryptography")
oltpMetrics.cryptoAlgorithmsHistogram, _ = meter.Int64Histogram("crypto.algorithms.req_time", metric.WithDescription("The time taken to run a crypto algorithms request (ms)"))
oltpMetrics.downloadRulesetHistogram, _ = meter.Int64Histogram("crypto.rulesets.download_time", metric.WithDescription("The time taken to download a ruleset (ms)"))
oltpMetrics.downloadRulesetCounter, _ = meter.Int64Counter("crypto.rulesets.downloaded", metric.WithDescription("The number of downloaded rulesets"))
}

// ConvertPurlRequestToComponentDTO converts a legacy PurlRequest to ComponentDTO slice.
Expand Down
Loading
Loading