Repository: google/osv-scanner

Issue:
Make it clearer that the `--experimental-plugins` flag disables existing plugins
Couple of things that need to be done here:
- Update the usage text of the flag to mention that we are  https://github.com/google/osv-scanner/blob/e28c9bc59852fb4599e79077e46c73d110066853/cmd/osv-scanner/internal/helper/flags.go#L172
- Update documentation to list out the default set of plugin presets we use, so people can reenabled it if they want to using and add additional plugins
- When using the flag, log out the default preset that was disabled, so it's easy for users to know which preset to re-add, and they are more aware of this behavior.

Hints from issue comments:
@G-Rath: @another-rex I wonder if it would be worth adding an `--extra-plugins` flag, as this sounds like a pretty common situation? @another-rex: Yeah that makes sense, rather than forcing users to re-enable everything from scratch.@G-Rath: and we could rename `plugins` to `--only-plugins` too, to make it clearer@G-Rath: actually @another-rex I'm now thinking it might be cleaner if we make this the default of `plugins`, and either have a special `default` preset that you can pass to `disable-plugins` or a new `--no-default-plugins`.

That way we won't have to deal with "what if you provide both `--only-plugins` and `--extra-plugins`"@another-rex: Yeah, I like the `--no-default-plugins` flag. Having the default plugins enabled was how some people I asked to use this flag assumed it  worked as well, so this would make sense.

Solution patch:
diff --git a/cmd/osv-scanner/internal/helper/flags.go b/cmd/osv-scanner/internal/helper/flags.go
index af6c4773..bc68c195 100644
--- a/cmd/osv-scanner/internal/helper/flags.go
+++ b/cmd/osv-scanner/internal/helper/flags.go
@@ -184,5 +184,9 @@ func BuildCommonScanFlags(defaultExtractors []string) []cli.Flag {
 			Name:  "experimental-disable-plugins",
 			Usage: "list of specific plugins and presets of plugins to not use",
 		},
+		&cli.BoolFlag{
+			Name:  "experimental-no-default-plugins",
+			Usage: "disable default plugins, instead using only those enabled by --experimental-plugins",
+		},
 	}
 }
diff --git a/cmd/osv-scanner/internal/helper/getters.go b/cmd/osv-scanner/internal/helper/getters.go
index f94e9290..49d40af7 100644
--- a/cmd/osv-scanner/internal/helper/getters.go
+++ b/cmd/osv-scanner/internal/helper/getters.go
@@ -51,7 +51,8 @@ func GetCommonScannerActions(cmd *cli.Command, scanLicensesAllowlist []string) o
 
 func GetExperimentalScannerActions(cmd *cli.Command) osvscanner.ExperimentalScannerActions {
 	return osvscanner.ExperimentalScannerActions{
-		PluginsEnabled:  cmd.StringSlice("experimental-plugins"),
-		PluginsDisabled: cmd.StringSlice("experimental-disable-plugins"),
+		PluginsEnabled:    cmd.StringSlice("experimental-plugins"),
+		PluginsDisabled:   cmd.StringSlice("experimental-disable-plugins"),
+		PluginsNoDefaults: cmd.Bool("experimental-no-default-plugins"),
 	}
 }
diff --git a/cmd/osv-scanner/scan/image/__snapshots__/command_test.snap b/cmd/osv-scanner/scan/image/__snapshots__/command_test.snap
index 4f9c95d8..86b74200 100755
--- a/cmd/osv-scanner/scan/image/__snapshots__/command_test.snap
+++ b/cmd/osv-scanner/scan/image/__snapshots__/command_test.snap
@@ -109,29 +109,68 @@ No issues found
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out - 1]
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out - 1]
+Checking if docker image ("alpine:non-existent-tag") exists locally...
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out - 2]
+Docker command exited with code ("/usr/bin/docker pull -q alpine:non-existent-tag"): 1
+STDERR:
+> Error response from daemon: manifest for alpine:non-existent-tag not found: manifest unknown: manifest unknown
+failed to pull container image: failed to run docker command
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out#01 - 1]
+Checking if docker image ("alpine:non-existent-tag") exists locally...
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out#01 - 2]
+Docker command exited with code ("/usr/bin/docker pull -q alpine:non-existent-tag"): 1
+STDERR:
+> Error response from daemon: manifest for alpine:non-existent-tag not found: manifest unknown: manifest unknown
+failed to pull container image: failed to run docker command
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out_with_presets - 1]
+Checking if docker image ("alpine:non-existent-tag") exists locally...
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out_with_presets - 2]
+Docker command exited with code ("/usr/bin/docker pull -q alpine:non-existent-tag"): 1
+STDERR:
+> Error response from daemon: manifest for alpine:non-existent-tag not found: manifest unknown: manifest unknown
+failed to pull container image: failed to run docker command
+
+---
+
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out - 1]
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out - 2]
 at least one extractor must be enabled
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out#01 - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out#01 - 1]
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out#01 - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out#01 - 2]
 at least one extractor must be enabled
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out_with_presets - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out_with_presets - 1]
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out_with_presets - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out_with_presets - 2]
 at least one extractor must be enabled
 
 ---
diff --git a/cmd/osv-scanner/scan/source/__snapshots__/command_test.snap b/cmd/osv-scanner/scan/source/__snapshots__/command_test.snap
index 2288fd83..4de3dd6d 100755
--- a/cmd/osv-scanner/scan/source/__snapshots__/command_test.snap
+++ b/cmd/osv-scanner/scan/source/__snapshots__/command_test.snap
@@ -1689,45 +1689,220 @@ Total 0 packages affected by 0 known vulnerabilities (0 Critical, 0 High, 0 Medi
 
 ---
 
-[TestCommand_ExplicitExtractors/empty_plugins_flag_does_nothing - 1]
+[TestCommand_ExplicitExtractors_WithDefaults/empty_plugins_flag_does_nothing - 1]
 
 ---
 
-[TestCommand_ExplicitExtractors/empty_plugins_flag_does_nothing - 2]
+[TestCommand_ExplicitExtractors_WithDefaults/empty_plugins_flag_does_nothing - 2]
 Incorrect Usage: flag needs an argument: --experimental-plugins=
 
 flag needs an argument: --experimental-plugins=
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out_specified_individually - 1]
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out_specified_individually - 1]
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out_specified_individually - 2]
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out_specified_individually - 2]
+No package sources found, --help for usage information.
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out_specified_together - 1]
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out_specified_together - 2]
+No package sources found, --help for usage information.
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out_with_presets - 1]
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/extractors_cancelled_out_with_presets - 2]
+No package sources found, --help for usage information.
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_directory_with_a_couple_of_specific_extractors_enabled_individually - 1]
+Scanning dir ./testdata/locks-many
+Scanned <rootdir>/testdata/locks-many/Gemfile.lock file and found 1 package
+Scanned <rootdir>/testdata/locks-many/alpine.cdx.xml file and found 15 packages
+Scanned <rootdir>/testdata/locks-many/composer.lock file and found 1 package
+Scanned <rootdir>/testdata/locks-many/package-lock.json file and found 1 package
+Scanned <rootdir>/testdata/locks-many/yarn.lock file and found 1 package
+Filtered 1 local/unscannable package/s from the scan.
+Loaded filter from: <rootdir>/testdata/locks-many/osv-scanner.toml
+CVE-2025-26519 has been filtered out because: Test manifest file (alpine.cdx.xml)
+GHSA-whgm-jr23-g3j9 and 1 alias have been filtered out because: Test manifest file
+Filtered 2 vulnerabilities from output
+No issues found
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_directory_with_a_couple_of_specific_extractors_enabled_individually - 2]
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_directory_with_a_couple_of_specific_extractors_enabled_specified_together - 1]
+Scanning dir ./testdata/locks-many
+Scanned <rootdir>/testdata/locks-many/Gemfile.lock file and found 1 package
+Scanned <rootdir>/testdata/locks-many/alpine.cdx.xml file and found 15 packages
+Scanned <rootdir>/testdata/locks-many/composer.lock file and found 1 package
+Scanned <rootdir>/testdata/locks-many/package-lock.json file and found 1 package
+Scanned <rootdir>/testdata/locks-many/yarn.lock file and found 1 package
+Filtered 1 local/unscannable package/s from the scan.
+Loaded filter from: <rootdir>/testdata/locks-many/osv-scanner.toml
+CVE-2025-26519 has been filtered out because: Test manifest file (alpine.cdx.xml)
+GHSA-whgm-jr23-g3j9 and 1 alias have been filtered out because: Test manifest file
+Filtered 2 vulnerabilities from output
+No issues found
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_directory_with_a_couple_of_specific_extractors_enabled_specified_together - 2]
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_directory_with_an_extractor_that_does_not_exist - 1]
+Scanning dir ./testdata/locks-many
+Scanned <rootdir>/testdata/locks-many/Gemfile.lock file and found 1 package
+Scanned <rootdir>/testdata/locks-many/alpine.cdx.xml file and found 15 packages
+Scanned <rootdir>/testdata/locks-many/composer.lock file and found 1 package
+Scanned <rootdir>/testdata/locks-many/package-lock.json file and found 1 package
+Scanned <rootdir>/testdata/locks-many/yarn.lock file and found 1 package
+Filtered 1 local/unscannable package/s from the scan.
+Loaded filter from: <rootdir>/testdata/locks-many/osv-scanner.toml
+CVE-2025-26519 has been filtered out because: Test manifest file (alpine.cdx.xml)
+GHSA-whgm-jr23-g3j9 and 1 alias have been filtered out because: Test manifest file
+Filtered 2 vulnerabilities from output
+Total 0 packages affected by 0 known vulnerabilities (0 Critical, 0 High, 0 Medium, 0 Low, 0 Unknown) from 0 ecosystems.
+0 vulnerabilities can be fixed.
+
+
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_directory_with_an_extractor_that_does_not_exist - 2]
+not an exact name for a plugin: "custom/extractor"
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_directory_with_one_specific_extractor_disabled - 1]
+Scanning dir ./testdata/locks-many
+Scanned <rootdir>/testdata/locks-many/Gemfile.lock file and found 1 package
+Scanned <rootdir>/testdata/locks-many/alpine.cdx.xml file and found 15 packages
+Scanned <rootdir>/testdata/locks-many/composer.lock file and found 1 package
+Scanned <rootdir>/testdata/locks-many/yarn.lock file and found 1 package
+Filtered 1 local/unscannable package/s from the scan.
+Loaded filter from: <rootdir>/testdata/locks-many/osv-scanner.toml
+CVE-2025-26519 has been filtered out because: Test manifest file (alpine.cdx.xml)
+Filtered 1 vulnerability from output
+No issues found
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_directory_with_one_specific_extractor_disabled - 2]
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_directory_with_one_specific_extractor_enabled_and_the_defaults - 1]
+Scanning dir ./testdata/locks-many
+Scanned <rootdir>/testdata/locks-many/Gemfile.lock file and found 1 package
+Scanned <rootdir>/testdata/locks-many/alpine.cdx.xml file and found 15 packages
+Scanned <rootdir>/testdata/locks-many/composer.lock file and found 1 package
+Scanned <rootdir>/testdata/locks-many/package-lock.json file and found 1 package
+Scanned <rootdir>/testdata/locks-many/yarn.lock file and found 1 package
+Filtered 1 local/unscannable package/s from the scan.
+Loaded filter from: <rootdir>/testdata/locks-many/osv-scanner.toml
+CVE-2025-26519 has been filtered out because: Test manifest file (alpine.cdx.xml)
+GHSA-whgm-jr23-g3j9 and 1 alias have been filtered out because: Test manifest file
+Filtered 2 vulnerabilities from output
+No issues found
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_directory_with_one_specific_extractor_enabled_and_the_defaults - 2]
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_file_with_one_different_extractor_enabled - 1]
+Scanning dir ./testdata/locks-many/composer.lock
+Scanned <rootdir>/testdata/locks-many/composer.lock file and found 1 package
+Loaded filter from: <rootdir>/testdata/locks-many/osv-scanner.toml
+No issues found
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_file_with_one_different_extractor_enabled - 2]
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_file_with_one_specific_extractor_enabled - 1]
+Scanning dir ./testdata/locks-many/package-lock.json
+Scanned <rootdir>/testdata/locks-many/package-lock.json file and found 1 package
+Loaded filter from: <rootdir>/testdata/locks-many/osv-scanner.toml
+GHSA-whgm-jr23-g3j9 and 1 alias have been filtered out because: Test manifest file
+Filtered 1 vulnerability from output
+No issues found
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_file_with_one_specific_extractor_enabled - 2]
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_file_with_parse_as_but_specific_extractor_disabled - 1]
+
+---
+
+[TestCommand_ExplicitExtractors_WithDefaults/scanning_file_with_parse_as_but_specific_extractor_disabled - 2]
+could not determine extractor, requested package-lock.json
+
+---
+
+[TestCommand_ExplicitExtractors_WithoutDefaults/empty_plugins_flag_does_nothing - 1]
+
+---
+
+[TestCommand_ExplicitExtractors_WithoutDefaults/empty_plugins_flag_does_nothing - 2]
+Incorrect Usage: flag needs an argument: --experimental-plugins=
+
+flag needs an argument: --experimental-plugins=
+
+---
+
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out_specified_individually - 1]
+
+---
+
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out_specified_individually - 2]
 at least one extractor must be enabled
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out_specified_together - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out_specified_together - 1]
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out_specified_together - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out_specified_together - 2]
 at least one extractor must be enabled
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out_with_presets - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out_with_presets - 1]
 
 ---
 
-[TestCommand_ExplicitExtractors/extractors_cancelled_out_with_presets - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/extractors_cancelled_out_with_presets - 2]
 at least one extractor must be enabled
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_directory_with_a_couple_of_specific_extractors_enabled_individually - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_directory_with_a_couple_of_specific_extractors_enabled_individually - 1]
 Scanning dir ./testdata/locks-many
 Scanned <rootdir>/testdata/locks-many/composer.lock file and found 1 package
 Scanned <rootdir>/testdata/locks-many/package-lock.json file and found 1 package
@@ -1738,11 +1913,11 @@ No issues found
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_directory_with_a_couple_of_specific_extractors_enabled_individually - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_directory_with_a_couple_of_specific_extractors_enabled_individually - 2]
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_directory_with_a_couple_of_specific_extractors_enabled_specified_together - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_directory_with_a_couple_of_specific_extractors_enabled_specified_together - 1]
 Scanning dir ./testdata/locks-many
 Scanned <rootdir>/testdata/locks-many/composer.lock file and found 1 package
 Scanned <rootdir>/testdata/locks-many/package-lock.json file and found 1 package
@@ -1753,11 +1928,11 @@ No issues found
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_directory_with_a_couple_of_specific_extractors_enabled_specified_together - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_directory_with_a_couple_of_specific_extractors_enabled_specified_together - 2]
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_directory_with_an_extractor_that_does_not_exist - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_directory_with_an_extractor_that_does_not_exist - 1]
 Scanning dir ./testdata/locks-many
 Scanned <rootdir>/testdata/locks-many/package-lock.json file and found 1 package
 Loaded filter from: <rootdir>/testdata/locks-many/osv-scanner.toml
@@ -1770,12 +1945,12 @@ Total 0 packages affected by 0 known vulnerabilities (0 Critical, 0 High, 0 Medi
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_directory_with_an_extractor_that_does_not_exist - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_directory_with_an_extractor_that_does_not_exist - 2]
 not an exact name for a plugin: "custom/extractor"
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_directory_with_one_specific_extractor_disabled - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_directory_with_one_specific_extractor_disabled - 1]
 Scanning dir ./testdata/locks-many
 Scanned <rootdir>/testdata/locks-many/Gemfile.lock file and found 1 package
 Scanned <rootdir>/testdata/locks-many/alpine.cdx.xml file and found 15 packages
@@ -1789,11 +1964,11 @@ No issues found
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_directory_with_one_specific_extractor_disabled - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_directory_with_one_specific_extractor_disabled - 2]
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_directory_with_one_specific_extractor_enabled - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_directory_with_one_specific_extractor_enabled_and_no_defaults - 1]
 Scanning dir ./testdata/locks-many
 Scanned <rootdir>/testdata/locks-many/package-lock.json file and found 1 package
 Loaded filter from: <rootdir>/testdata/locks-many/osv-scanner.toml
@@ -1803,21 +1978,21 @@ No issues found
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_directory_with_one_specific_extractor_enabled - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_directory_with_one_specific_extractor_enabled_and_no_defaults - 2]
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_file_with_one_different_extractor_enabled - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_file_with_one_different_extractor_enabled - 1]
 Scanning dir ./testdata/locks-many/composer.lock
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_file_with_one_different_extractor_enabled - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_file_with_one_different_extractor_enabled - 2]
 No package sources found, --help for usage information.
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_file_with_one_specific_extractor_enabled - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_file_with_one_specific_extractor_enabled - 1]
 Scanning dir ./testdata/locks-many/package-lock.json
 Scanned <rootdir>/testdata/locks-many/package-lock.json file and found 1 package
 Loaded filter from: <rootdir>/testdata/locks-many/osv-scanner.toml
@@ -1827,15 +2002,15 @@ No issues found
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_file_with_one_specific_extractor_enabled - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_file_with_one_specific_extractor_enabled - 2]
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_file_with_parse_as_but_specific_extractor_disabled - 1]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_file_with_parse_as_but_specific_extractor_disabled - 1]
 
 ---
 
-[TestCommand_ExplicitExtractors/scanning_file_with_parse_as_but_specific_extractor_disabled - 2]
+[TestCommand_ExplicitExtractors_WithoutDefaults/scanning_file_with_parse_as_but_specific_extractor_disabled - 2]
 could not determine extractor, requested package-lock.json
 
 ---
diff --git a/docs/manual-plugin-selection.md b/docs/manual-plugin-selection.md
index 83d0c1c6..8e3de4b6 100644
--- a/docs/manual-plugin-selection.md
+++ b/docs/manual-plugin-selection.md
@@ -30,8 +30,9 @@ you can manually enable or disable them.
 
 You can control which plugins to run using the following flags:
 
-- `--experimental-plugins`: Enables a comma-separated list of specific plugins.
+- `--experimental-plugins`: Enables a comma-separated list of specific plugins that will be used along with the default plugins for the command being run
 - `--experimental-disable-plugins`: Disables a comma-separated list of specific plugins.
+- `--experimental-no-default-plugins`: Excludes the default plugins for the command being run from being automatically included
 
 For a full list of available plugin names, see OSV-Scalibr's documentation here:
 https://github.com/google/osv-scalibr/blob/main/docs/supported_inventory_types.md
diff --git a/docs/migrating-from-scalibr.md b/docs/migrating-from-scalibr.md
index 7af324f1..b9a3c2c1 100644
--- a/docs/migrating-from-scalibr.md
+++ b/docs/migrating-from-scalibr.md
@@ -57,7 +57,10 @@ In `osv-scanner`, you can achieve the same by using the `--experimental-plugins`
 osv-scanner --experimental-plugins python/pip,go/gomod,go/govulncheck /path/to/your/project
 ```
 
-`osv-scanner` also allows you to disable default plugins with `--experimental-disable-plugins`.
+`osv-scanner` lets you exclude its default plugins with `--experimental-no-default-plugins`, for when you want to only
+run specific plugins.
+
+`osv-scanner` also allows you to disable specific plugins with `--experimental-disable-plugins`.
 
 For more details on manual plugin selection in `osv-scanner`, see the [manual plugin selection documentation](manual-plugin-selection.md).
 
diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go
index 5f1cb515..ea00728e 100644
--- a/pkg/osvscanner/osvscanner.go
+++ b/pkg/osvscanner/osvscanner.go
@@ -71,8 +71,9 @@ type ScannerActions struct {
 type ExperimentalScannerActions struct {
 	TransitiveScanningActions
 
-	PluginsEnabled  []string
-	PluginsDisabled []string
+	PluginsEnabled    []string
+	PluginsDisabled   []string
+	PluginsNoDefaults bool
 }
 
 type TransitiveScanningActions struct {
diff --git a/pkg/osvscanner/scan.go b/pkg/osvscanner/scan.go
index 38e124aa..7077f454 100644
--- a/pkg/osvscanner/scan.go
+++ b/pkg/osvscanner/scan.go
@@ -63,8 +63,8 @@ func configurePlugins(plugins []plugin.Plugin, accessors ExternalAccessors, acti
 }
 
 func getPlugins(defaultPlugins []string, accessors ExternalAccessors, actions ScannerActions) []plugin.Plugin {
-	if len(actions.PluginsEnabled) == 0 {
-		actions.PluginsEnabled = defaultPlugins
+	if !actions.PluginsNoDefaults {
+		actions.PluginsEnabled = append(actions.PluginsEnabled, defaultPlugins...)
 	}
 
 	plugins := scalibrplugin.Resolve(actions.PluginsEnabled, actions.PluginsDisabled)



Test patch:
diff --git a/cmd/osv-scanner/scan/image/command_test.go b/cmd/osv-scanner/scan/image/command_test.go
index d291ea35..653af253 100644
--- a/cmd/osv-scanner/scan/image/command_test.go
+++ b/cmd/osv-scanner/scan/image/command_test.go
@@ -11,7 +11,7 @@ import (
 	"github.com/google/osv-scanner/v2/internal/testutility"
 )
 
-func TestCommand_ExplicitExtractors(t *testing.T) {
+func TestCommand_ExplicitExtractors_WithDefaults(t *testing.T) {
 	t.Parallel()
 
 	tests := []testcmd.Case{
@@ -47,6 +47,59 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 			Exit: 127,
 		},
 	}
+	for _, tt := range tests {
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+
+			// Only test on linux, and mac/windows CI/CD does not come with docker preinstalled
+			if runtime.GOOS != "linux" {
+				testutility.Skip(t, "Skipping Docker-based test as only Linux has Docker installed in CI")
+			}
+
+			testcmd.RunAndMatchSnapshots(t, tt)
+		})
+	}
+}
+
+func TestCommand_ExplicitExtractors_WithoutDefaults(t *testing.T) {
+	t.Parallel()
+
+	tests := []testcmd.Case{
+		{
+			Name: "extractors_cancelled_out",
+			Args: []string{
+				"", "image",
+				"--experimental-plugins=sbom/spdx",
+				"--experimental-plugins=sbom/cdx",
+				"--experimental-disable-plugins=sbom",
+				"--experimental-no-default-plugins",
+				"alpine:non-existent-tag",
+			},
+			Exit: 127,
+		},
+		{
+			Name: "extractors_cancelled_out_with_presets",
+			Args: []string{
+				"", "image",
+				"--experimental-plugins=sbom",
+				"--experimental-disable-plugins=sbom",
+				"--experimental-no-default-plugins",
+				"alpine:non-existent-tag",
+			},
+			Exit: 127,
+		},
+		{
+			Name: "extractors_cancelled_out",
+			Args: []string{
+				"", "image",
+				"--experimental-plugins=sbom/spdx,sbom/cdx",
+				"--experimental-disable-plugins=sbom",
+				"--experimental-no-default-plugins",
+				"alpine:non-existent-tag",
+			},
+			Exit: 127,
+		},
+	}
 	for _, tt := range tests {
 		t.Run(tt.Name, func(t *testing.T) {
 			t.Parallel()
diff --git a/cmd/osv-scanner/scan/source/command_test.go b/cmd/osv-scanner/scan/source/command_test.go
index a00c0124..7f404552 100644
--- a/cmd/osv-scanner/scan/source/command_test.go
+++ b/cmd/osv-scanner/scan/source/command_test.go
@@ -350,7 +350,7 @@ func TestCommand_JavareachArchive(t *testing.T) {
 	}
 }
 
-func TestCommand_ExplicitExtractors(t *testing.T) {
+func TestCommand_ExplicitExtractors_WithDefaults(t *testing.T) {
 	t.Parallel()
 
 	tests := []testcmd.Case{
@@ -367,6 +367,141 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 				"--experimental-plugins=sbom/cdx",
 				"--experimental-disable-plugins=sbom",
 			},
+			Exit: 128,
+		},
+		{
+			Name: "extractors_cancelled_out_specified_together",
+			Args: []string{
+				"", "source",
+				"--experimental-plugins=sbom/spdx,sbom/cdx",
+				"--experimental-disable-plugins=sbom",
+			},
+			Exit: 128,
+		},
+		{
+			Name: "extractors_cancelled_out_with_presets",
+			Args: []string{
+				"", "source",
+				"--experimental-plugins=sbom",
+				"--experimental-disable-plugins=sbom",
+			},
+			Exit: 128,
+		},
+		{
+			// this will scan all the lockfiles as we have not explicitly disabled the
+			// default extractors for any of the other lockfiles
+			Name: "scanning_directory_with_one_specific_extractor_enabled_and_the_defaults",
+			Args: []string{
+				"", "source",
+				"--experimental-plugins=javascript/packagelockjson",
+				"./testdata/locks-many",
+			},
+			Exit: 0,
+		},
+		{
+			Name: "scanning_directory_with_an_extractor_that_does_not_exist",
+			Args: []string{
+				"", "source",
+				"--experimental-plugins=javascript/packagelockjson",
+				"--experimental-plugins=custom/extractor",
+				"--experimental-disable-plugins=custom/anotherextractor",
+				"./testdata/locks-many",
+			},
+			Exit: 127,
+		},
+		{
+			// this will scan just the package-lock.json and composer.lock files as
+			// we've not enabled extractors for any of the other lockfiles
+			Name: "scanning_directory_with_a_couple_of_specific_extractors_enabled_individually",
+			Args: []string{
+				"", "source",
+				"--experimental-plugins=javascript/packagelockjson",
+				"--experimental-plugins=php/composerlock",
+				"./testdata/locks-many",
+			},
+			Exit: 0,
+		},
+		{
+			// this will scan just the package-lock.json and composer.lock files as
+			// we've not enabled extractors for any of the other lockfiles
+			Name: "scanning_directory_with_a_couple_of_specific_extractors_enabled_specified_together",
+			Args: []string{
+				"", "source",
+				"--experimental-plugins=javascript/packagelockjson,php/composerlock",
+				"./testdata/locks-many",
+			},
+			Exit: 0,
+		},
+		{
+			// this should result in all files within the directory being scanned
+			// except for the package-lock.json
+			Name: "scanning_directory_with_one_specific_extractor_disabled",
+			Args: []string{
+				"", "source",
+				"--experimental-disable-plugins=javascript/packagelockjson",
+				"./testdata/locks-many",
+			},
+			Exit: 0,
+		},
+		{
+			// this will scan just the package lock, since we're requested that file specifically
+			Name: "scanning_file_with_one_specific_extractor_enabled",
+			Args: []string{
+				"", "source",
+				"--experimental-plugins=javascript/packagelockjson",
+				"./testdata/locks-many/package-lock.json",
+			},
+			Exit: 0,
+		},
+		{
+			// this will result in no issues since we have left the default plugins enabled
+			Name: "scanning_file_with_one_different_extractor_enabled",
+			Args: []string{
+				"", "source",
+				"--experimental-plugins=javascript/packagelockjson",
+				"./testdata/locks-many/composer.lock",
+			},
+			Exit: 0,
+		},
+		{
+			// this will result in an error about not being able to determine the extractor
+			// since we've requested the file to be parsed with a specific extractor
+			// that we've also disabled
+			Name: "scanning_file_with_parse_as_but_specific_extractor_disabled",
+			Args: []string{
+				"", "source",
+				"--experimental-disable-plugins=javascript/packagelockjson",
+				"-L", "package-lock.json:./testdata/locks-many/composer.lock",
+			},
+			Exit: 127,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+			testcmd.RunAndMatchSnapshots(t, tt)
+		})
+	}
+}
+
+func TestCommand_ExplicitExtractors_WithoutDefaults(t *testing.T) {
+	t.Parallel()
+
+	tests := []testcmd.Case{
+		{
+			Name: "empty_plugins_flag_does_nothing",
+			Args: []string{"", "source", "--experimental-no-default-plugins", "--experimental-plugins="},
+			Exit: 127,
+		},
+		{
+			Name: "extractors_cancelled_out_specified_individually",
+			Args: []string{
+				"", "source",
+				"--experimental-plugins=sbom/spdx",
+				"--experimental-plugins=sbom/cdx",
+				"--experimental-disable-plugins=sbom",
+				"--experimental-no-default-plugins",
+			},
 			Exit: 127,
 		},
 		{
@@ -375,6 +510,7 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 				"", "source",
 				"--experimental-plugins=sbom/spdx,sbom/cdx",
 				"--experimental-disable-plugins=sbom",
+				"--experimental-no-default-plugins",
 			},
 			Exit: 127,
 		},
@@ -384,16 +520,18 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 				"", "source",
 				"--experimental-plugins=sbom",
 				"--experimental-disable-plugins=sbom",
+				"--experimental-no-default-plugins",
 			},
 			Exit: 127,
 		},
 		{
-			// this will scan just the package-lock.json file as we've not enabled
-			// extractors for any of the other lockfiles
-			Name: "scanning_directory_with_one_specific_extractor_enabled",
+			// this will scan just the package-lock.json file as we've explicitly
+			// disabled the default extractors for any of the other lockfiles
+			Name: "scanning_directory_with_one_specific_extractor_enabled_and_no_defaults",
 			Args: []string{
 				"", "source",
 				"--experimental-plugins=javascript/packagelockjson",
+				"--experimental-no-default-plugins",
 				"./testdata/locks-many",
 			},
 			Exit: 0,
@@ -405,6 +543,7 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 				"--experimental-plugins=javascript/packagelockjson",
 				"--experimental-plugins=custom/extractor",
 				"--experimental-disable-plugins=custom/anotherextractor",
+				"--experimental-no-default-plugins",
 				"./testdata/locks-many",
 			},
 			Exit: 127,
@@ -417,6 +556,7 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 				"", "source",
 				"--experimental-plugins=javascript/packagelockjson",
 				"--experimental-plugins=php/composerlock",
+				"--experimental-no-default-plugins",
 				"./testdata/locks-many",
 			},
 			Exit: 0,
@@ -428,6 +568,7 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 			Args: []string{
 				"", "source",
 				"--experimental-plugins=javascript/packagelockjson,php/composerlock",
+				"--experimental-no-default-plugins",
 				"./testdata/locks-many",
 			},
 			Exit: 0,
@@ -439,6 +580,7 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 			Args: []string{
 				"", "source",
 				"--experimental-disable-plugins=javascript/packagelockjson",
+				"--experimental-no-default-plugins",
 				"./testdata/locks-many",
 			},
 			Exit: 0,
@@ -450,6 +592,7 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 			Args: []string{
 				"", "source",
 				"--experimental-plugins=javascript/packagelockjson",
+				"--experimental-no-default-plugins",
 				"./testdata/locks-many/package-lock.json",
 			},
 			Exit: 0,
@@ -461,6 +604,7 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 			Args: []string{
 				"", "source",
 				"--experimental-plugins=javascript/packagelockjson",
+				"--experimental-no-default-plugins",
 				"./testdata/locks-many/composer.lock",
 			},
 			Exit: 128,
@@ -473,6 +617,7 @@ func TestCommand_ExplicitExtractors(t *testing.T) {
 			Args: []string{
 				"", "source",
 				"--experimental-disable-plugins=javascript/packagelockjson",
+				"--experimental-no-default-plugins",
 				"-L", "package-lock.json:./testdata/locks-many/composer.lock",
 			},
 			Exit: 127,