From 06dc8d5930b6f04352b442768cbdba446c57d754 Mon Sep 17 00:00:00 2001 From: eeisegn Date: Mon, 23 Mar 2026 17:20:14 +0000 Subject: [PATCH 1/3] fix client-side flags override issue --- CHANGELOG.md | 6 ++ config/app-config-prod.json | 1 + pkg/config/server_config.go | 2 + pkg/service/scanning_service.go | 10 +++ pkg/service/scanning_service_test.go | 101 +++++++++++++++++++++++++++ 5 files changed, 120 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77fc344..550fa2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.6.5] - 2026-03-24 +### Fixed +- Fixed an issue where client-side flags could override server-side flags. + - Client-side overrides now need an explicit enablement flag: `AllowFlagsOverride` + ## [1.6.4] - 2026-03-23 ### Added - Add support for single worker scan timeout HTTP response handling (504) @@ -200,3 +205,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [1.6.2]: https://github.com/scanoss/api.go/compare/v1.6.1...v1.6.2 [1.6.3]: https://github.com/scanoss/api.go/compare/v1.6.2...v1.6.3 [1.6.4]: https://github.com/scanoss/api.go/compare/v1.6.3...v1.6.4 +[1.6.5]: https://github.com/scanoss/api.go/compare/v1.6.4...v1.6.5 diff --git a/config/app-config-prod.json b/config/app-config-prod.json index de38f89..d990fae 100644 --- a/config/app-config-prod.json +++ b/config/app-config-prod.json @@ -25,6 +25,7 @@ "ScanBinary": "scanoss", "ScanDebug": false, "ScanFlags": 0, + "AllowFlagsOverride": false, "ScanTimeout": 180, "WfpGrouping": 3, "Workers": 2, diff --git a/pkg/config/server_config.go b/pkg/config/server_config.go index b15cdec..56e0118 100644 --- a/pkg/config/server_config.go +++ b/pkg/config/server_config.go @@ -59,6 +59,7 @@ type ServerConfig struct { ScanKbName string `env:"SCAN_KB_NAME"` // KB name passed as "-n" parameter to the scanoss command ScanDebug bool `env:"SCAN_DEBUG"` // true/false ScanFlags int `env:"SCAN_ENGINE_FLAGS"` // Default flags to use when scanning + AllowFlagsOverride bool `env:"SCAN_ALLOW_FLAGS_OVERRIDE"` // Allow clients to override the default flags ScanTimeout int `env:"SCAN_ENGINE_TIMEOUT"` // timeout for waiting for the scan engine to respond WfpGrouping int `env:"SCAN_WFP_GROUPING"` // number of WFP to group into a single scan engine command Workers int `env:"SCAN_WORKERS"` // Number of concurrent workers to use per scan request @@ -138,6 +139,7 @@ func setServerConfigDefaults(cfg *ServerConfig) { cfg.Scanning.MinSnippetHits = 0 // Lets the engine decide on minimum snippet hits based on the file total lines cfg.Scanning.MinSnippetLines = 0 // Lets the engine decide on minimum snippet hits on the file total lines cfg.Scanning.HonourFileExts = true + cfg.Scanning.AllowFlagsOverride = false // Disallow clients overriding the default flags if it's set server-side } // LoadFile loads the specified file and returns its contents in a string array. diff --git a/pkg/service/scanning_service.go b/pkg/service/scanning_service.go index 985a9d8..1cdb5bb 100644 --- a/pkg/service/scanning_service.go +++ b/pkg/service/scanning_service.go @@ -184,6 +184,16 @@ func (s APIService) getConfigFromRequest(r *http.Request, zs *zap.SugaredLogger) if len(dbName) == 0 { dbName = strings.TrimSpace(r.Header.Get("db_name")) } + if len(flags) > 0 && s.config.Scanning.ScanFlags > 0 { + if !s.config.Scanning.AllowFlagsOverride { + zs.Warnf("Ignoring flags (%v) in the request. Using flags from the server config: %v", + flags, s.config.Scanning.ScanFlags) + flags = "" // Clear the flags to use the server configuration + } else { + zs.Debugf("Using flags (%v) from the request instead of server config: %v", + flags, s.config.Scanning.ScanFlags) + } + } scanSettings := strings.TrimSpace(r.Header.Get("scanoss-settings")) // Check the header for scan settings if s.config.App.Trace { zs.Debugf("Header: %v, Form: %v, flags: %v, type: %v, assets: %v, db_name: %v, scanSettings: %v", diff --git a/pkg/service/scanning_service_test.go b/pkg/service/scanning_service_test.go index 21c845f..3576373 100644 --- a/pkg/service/scanning_service_test.go +++ b/pkg/service/scanning_service_test.go @@ -601,3 +601,104 @@ func TestScanDirectSingleSlow(t *testing.T) { }) } } + +func TestScanDirectSingleFlags(t *testing.T) { + err := zlog.NewSugaredDevLogger() + if err != nil { + t.Fatalf("an error '%s' was not expected when opening a sugared logger", err) + } + defer zlog.SyncZap() + myConfig := setupConfig(t) + myConfig.App.Trace = true + myConfig.Scanning.ScanDebug = true + myConfig.Scanning.ScanTimeout = 5 + apiService := NewAPIService(myConfig) + + fieldName := "file" + file := "./tests/fingers.wfp" + binary := "../../test-support/scanoss.sh" + + tests := []struct { + name string + serverFlags int + allowFlagsOverride bool + clientFlags string + want int + }{ + { + name: "Scanning - no flags", + serverFlags: 0, + allowFlagsOverride: false, + clientFlags: "", + want: http.StatusOK, + }, + { + name: "Scanning - client flags", + serverFlags: 0, + allowFlagsOverride: false, + clientFlags: "256", + want: http.StatusOK, + }, + { + name: "Scanning - server flags", + serverFlags: 1248, + allowFlagsOverride: false, + clientFlags: "", + want: http.StatusOK, + }, + { + name: "Scanning - server/clients flags - allowed", + serverFlags: 1248, + allowFlagsOverride: true, + clientFlags: "256", + want: http.StatusOK, + }, + { + name: "Scanning - server/clients flags - not allowed", + serverFlags: 1248, + allowFlagsOverride: false, + clientFlags: "256", + want: http.StatusOK, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + myConfig.Scanning.ScanFlags = test.serverFlags + myConfig.Scanning.AllowFlagsOverride = test.allowFlagsOverride + myConfig.Scanning.ScanBinary = binary + filePath := file + fieldName := fieldName + postBody := new(bytes.Buffer) + mw := multipart.NewWriter(postBody) + file, err := os.Open(filePath) + if err != nil { + t.Fatal(err) + } + writer, err := mw.CreateFormFile(fieldName, filePath) + if err != nil { + t.Fatal(err) + } + if _, err = io.Copy(writer, file); err != nil { + t.Fatal(err) + } + _ = mw.Close() // close the writer before making the request + + req := httptest.NewRequest(http.MethodPost, "http://localhost/scan/direct", postBody) + w := httptest.NewRecorder() + req.Header.Add("Content-Type", mw.FormDataContentType()) + if len(test.clientFlags) > 0 { + req.Header.Add("flags", test.clientFlags) + } + apiService.ScanDirect(w, req) + resp := w.Result() + body, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("an error was not expected when reading from request: %v", err) + } + assert.Equal(t, test.want, resp.StatusCode) + fmt.Println("Status: ", resp.StatusCode) + fmt.Println("Type: ", resp.Header.Get("Content-Type")) + fmt.Println("Body: ", string(body)) + }) + } +} From 914ef797c091bd980302bb77299aa9eeac2e2ef5 Mon Sep 17 00:00:00 2001 From: eeisegn Date: Mon, 23 Mar 2026 17:41:42 +0000 Subject: [PATCH 2/3] fix variable name shadowing --- pkg/service/scanning_service_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/service/scanning_service_test.go b/pkg/service/scanning_service_test.go index 3576373..a9947a8 100644 --- a/pkg/service/scanning_service_test.go +++ b/pkg/service/scanning_service_test.go @@ -613,9 +613,8 @@ func TestScanDirectSingleFlags(t *testing.T) { myConfig.Scanning.ScanDebug = true myConfig.Scanning.ScanTimeout = 5 apiService := NewAPIService(myConfig) - fieldName := "file" - file := "./tests/fingers.wfp" + filePath := "./tests/fingers.wfp" binary := "../../test-support/scanoss.sh" tests := []struct { @@ -666,8 +665,6 @@ func TestScanDirectSingleFlags(t *testing.T) { myConfig.Scanning.ScanFlags = test.serverFlags myConfig.Scanning.AllowFlagsOverride = test.allowFlagsOverride myConfig.Scanning.ScanBinary = binary - filePath := file - fieldName := fieldName postBody := new(bytes.Buffer) mw := multipart.NewWriter(postBody) file, err := os.Open(filePath) From cc122f65c709f810c71009c5b931cf47b4ee77d3 Mon Sep 17 00:00:00 2001 From: eeisegn Date: Tue, 24 Mar 2026 12:45:57 +0000 Subject: [PATCH 3/3] update test cases based on feedback --- pkg/service/scanning_service_test.go | 50 +++++++--------------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/pkg/service/scanning_service_test.go b/pkg/service/scanning_service_test.go index a9947a8..db068cd 100644 --- a/pkg/service/scanning_service_test.go +++ b/pkg/service/scanning_service_test.go @@ -602,7 +602,7 @@ func TestScanDirectSingleSlow(t *testing.T) { } } -func TestScanDirectSingleFlags(t *testing.T) { +func TestScanFlags(t *testing.T) { err := zlog.NewSugaredDevLogger() if err != nil { t.Fatalf("an error '%s' was not expected when opening a sugared logger", err) @@ -612,10 +612,6 @@ func TestScanDirectSingleFlags(t *testing.T) { myConfig.App.Trace = true myConfig.Scanning.ScanDebug = true myConfig.Scanning.ScanTimeout = 5 - apiService := NewAPIService(myConfig) - fieldName := "file" - filePath := "./tests/fingers.wfp" - binary := "../../test-support/scanoss.sh" tests := []struct { name string @@ -629,73 +625,51 @@ func TestScanDirectSingleFlags(t *testing.T) { serverFlags: 0, allowFlagsOverride: false, clientFlags: "", - want: http.StatusOK, + want: 0, }, { name: "Scanning - client flags", serverFlags: 0, allowFlagsOverride: false, clientFlags: "256", - want: http.StatusOK, + want: 256, }, { name: "Scanning - server flags", serverFlags: 1248, allowFlagsOverride: false, clientFlags: "", - want: http.StatusOK, + want: 1248, }, { name: "Scanning - server/clients flags - allowed", serverFlags: 1248, allowFlagsOverride: true, clientFlags: "256", - want: http.StatusOK, + want: 256, }, { name: "Scanning - server/clients flags - not allowed", serverFlags: 1248, allowFlagsOverride: false, clientFlags: "256", - want: http.StatusOK, + want: 1248, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { myConfig.Scanning.ScanFlags = test.serverFlags myConfig.Scanning.AllowFlagsOverride = test.allowFlagsOverride - myConfig.Scanning.ScanBinary = binary - postBody := new(bytes.Buffer) - mw := multipart.NewWriter(postBody) - file, err := os.Open(filePath) - if err != nil { - t.Fatal(err) - } - writer, err := mw.CreateFormFile(fieldName, filePath) - if err != nil { - t.Fatal(err) - } - if _, err = io.Copy(writer, file); err != nil { - t.Fatal(err) - } - _ = mw.Close() // close the writer before making the request - req := httptest.NewRequest(http.MethodPost, "http://localhost/scan/direct", postBody) - w := httptest.NewRecorder() - req.Header.Add("Content-Type", mw.FormDataContentType()) + req := httptest.NewRequest(http.MethodPost, "/scan/direct", nil) if len(test.clientFlags) > 0 { req.Header.Add("flags", test.clientFlags) } - apiService.ScanDirect(w, req) - resp := w.Result() - body, err := io.ReadAll(resp.Body) - if err != nil { - t.Fatalf("an error was not expected when reading from request: %v", err) - } - assert.Equal(t, test.want, resp.StatusCode) - fmt.Println("Status: ", resp.StatusCode) - fmt.Println("Type: ", resp.Header.Get("Content-Type")) - fmt.Println("Body: ", string(body)) + apiService := NewAPIService(myConfig) + cfg, err := apiService.getConfigFromRequest(req, zlog.S) + assert.NoError(t, err) + assert.Equal(t, test.want, cfg.flags) + fmt.Println("Flags: ", cfg.flags) }) } }