diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..0089e35e4
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "rust-analyzer.linkedProjects": [
+ "src/CargoCLock/Cargo.toml"
+ ]
+}
\ No newline at end of file
diff --git a/global.json b/global.json
index a99f94e05..aea164061 100644
--- a/global.json
+++ b/global.json
@@ -2,5 +2,8 @@
"sdk": {
"version": "8.0.408",
"rollForward": "latestMinor"
+ },
+ "msbuild-sdks": {
+ "Microsoft.Build.Cargo": "1.0.350-preview"
}
}
diff --git a/src/CargoCLock/.gitignore b/src/CargoCLock/.gitignore
new file mode 100644
index 000000000..08c4841f0
--- /dev/null
+++ b/src/CargoCLock/.gitignore
@@ -0,0 +1,3 @@
+bin/
+obj/
+target/
\ No newline at end of file
diff --git a/src/CargoCLock/Cargo.lock b/src/CargoCLock/Cargo.lock
new file mode 100644
index 000000000..14b047d7a
--- /dev/null
+++ b/src/CargoCLock/Cargo.lock
@@ -0,0 +1,497 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "cargo-lock"
+version = "10.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06acb4f71407ba205a07cb453211e0e6a67b21904e47f6ba1f9589e38f2e454"
+dependencies = [
+ "petgraph",
+ "semver",
+ "serde",
+ "toml",
+ "url",
+]
+
+[[package]]
+name = "cargo_c_lock"
+version = "0.1.0"
+dependencies = [
+ "cargo-lock",
+ "serde_json",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
+
+[[package]]
+name = "icu_collections"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
+
+[[package]]
+name = "icu_properties"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "potential_utf",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
+
+[[package]]
+name = "icu_provider"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "idna"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "litemap"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
+
+[[package]]
+name = "memchr"
+version = "2.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "petgraph"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+
+[[package]]
+name = "semver"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.140"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "syn"
+version = "2.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_write",
+ "winnow",
+]
+
+[[package]]
+name = "toml_write"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "url"
+version = "2.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "winnow"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "writeable"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
+
+[[package]]
+name = "yoke"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerotrie"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/src/CargoCLock/Cargo.toml b/src/CargoCLock/Cargo.toml
new file mode 100644
index 000000000..c99183550
--- /dev/null
+++ b/src/CargoCLock/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "cargo_c_lock"
+version = "0.1.0"
+edition = "2024"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+cargo-lock = { version = "10.1.0", features = ["dependency-tree"] }
+serde_json = "1.0.140"
diff --git a/src/CargoCLock/CargoCLock.proj b/src/CargoCLock/CargoCLock.proj
new file mode 100644
index 000000000..30319636a
--- /dev/null
+++ b/src/CargoCLock/CargoCLock.proj
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/CargoCLock/src/lib.rs b/src/CargoCLock/src/lib.rs
new file mode 100644
index 000000000..5ad09750b
--- /dev/null
+++ b/src/CargoCLock/src/lib.rs
@@ -0,0 +1,50 @@
+use cargo_lock::Lockfile;
+use std::{
+ ffi::{CStr, CString},
+ path::PathBuf,
+};
+
+// SAFETY: there is no other global function of this name
+#[unsafe(no_mangle)]
+pub extern "C" fn json(path_ptr: *const i8) -> *mut i8 {
+ // SAFETY: The caller must guarantee that path_ptr is a valid null-terminated C string.
+ let path_cstr = unsafe { CStr::from_ptr(path_ptr) };
+ let path: PathBuf = path_cstr.to_str().expect("Invalid UTF-8 in path").into();
+
+ let lockfile = Lockfile::load(path).unwrap();
+ let serialized = serde_json::to_string(&lockfile.packages).unwrap();
+
+ let json_cstring = CString::new(serialized).unwrap();
+ let json_ptr = json_cstring.into_raw();
+ json_ptr
+}
+
+// SAFETY: there is no other global function of this name
+#[unsafe(no_mangle)]
+pub extern "C" fn free(json_ptr: *mut i8) {
+ if json_ptr.is_null() {
+ return;
+ }
+
+ // SAFETY: for manual deallocation.
+ unsafe { drop(CString::from_raw(json_ptr)) };
+}
+
+#[test]
+fn test_json() {
+ // test-only: create the path argument, which would be caller-owned.
+ let path = std::env::current_dir().unwrap().join("Cargo.lock");
+ let path_cstring = CString::new(path.to_str().unwrap()).unwrap();
+ let path_ptr = path_cstring.as_ptr();
+
+ // Get a Rust-owned pointer to the JSON string.
+ let json_ptr = json(path_ptr);
+
+ // Get a reference to the contents so we can test the `free` call:
+ let json_cstr = unsafe { CStr::from_ptr(json_ptr) };
+ let json_str = json_cstr.to_str().unwrap();
+ assert!(json_str.contains("name"));
+
+ // Let Rust know it can clean up.
+ free(json_ptr);
+}
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 0eb245117..1c4e4924e 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -3,6 +3,7 @@
+ true
true
snupkg
diff --git a/src/Microsoft.ComponentDetection.Detectors/Microsoft.ComponentDetection.Detectors.csproj b/src/Microsoft.ComponentDetection.Detectors/Microsoft.ComponentDetection.Detectors.csproj
index a0fb3f5e4..ec31adcd7 100644
--- a/src/Microsoft.ComponentDetection.Detectors/Microsoft.ComponentDetection.Detectors.csproj
+++ b/src/Microsoft.ComponentDetection.Detectors/Microsoft.ComponentDetection.Detectors.csproj
@@ -20,7 +20,8 @@
-
+
+
@@ -33,4 +34,10 @@
+
+
+ Always
+
+
+
diff --git a/src/Microsoft.ComponentDetection.Detectors/rust/LockfileLoader.cs b/src/Microsoft.ComponentDetection.Detectors/rust/LockfileLoader.cs
new file mode 100644
index 000000000..0fb9852fe
--- /dev/null
+++ b/src/Microsoft.ComponentDetection.Detectors/rust/LockfileLoader.cs
@@ -0,0 +1,84 @@
+namespace Microsoft.ComponentDetection.Detectors.Rust;
+
+#pragma warning disable IDE0007 // Use implicit type
+#pragma warning disable SA1402 // File may only contain a single type
+
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+
+public sealed class LockfileLoader : IDisposable
+{
+ private readonly LockfileHandle lockfile;
+ private string lockfileString;
+
+ public LockfileLoader(string lockfilePath) => this.lockfile = Native.Json(lockfilePath);
+
+ public override string ToString()
+ {
+ this.lockfileString ??= this.lockfile.AsString();
+ return this.lockfileString;
+ }
+
+ public void Dispose()
+ {
+ this.lockfile.Dispose();
+ }
+}
+
+///
+/// Provides interop methods for working with the Rust-produced native cargo_c_lock library.
+///
+internal partial class Native
+{
+ [LibraryImport("cargo_c_lock", EntryPoint = "json", StringMarshalling = StringMarshalling.Utf8)]
+ [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
+ internal static partial LockfileHandle Json(string lockfilePath);
+
+ ///
+ /// Frees the memory associated with the specified JSON handle.
+ ///
+ /// The handle to the JSON memory to free.
+ [LibraryImport("cargo_c_lock", EntryPoint = "free")]
+ [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
+ internal static partial void Free(IntPtr jsonHandle);
+}
+
+internal class LockfileHandle : SafeHandle
+{
+ public LockfileHandle()
+ : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid
+ {
+ get { return this.handle == IntPtr.Zero; }
+ }
+
+ public string AsString()
+ {
+ int len = 0;
+ while (Marshal.ReadByte(this.handle, len) != 0)
+ {
+ ++len;
+ }
+
+ byte[] buffer = new byte[len];
+ Marshal.Copy(this.handle, buffer, 0, buffer.Length);
+ return Encoding.UTF8.GetString(buffer);
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ if (!this.IsInvalid)
+ {
+ Native.Free(this.handle);
+ }
+
+ return true;
+ }
+}
+
+#pragma warning restore SA1402 // File may only contain a single type
+#pragma warning restore IDE0007 // Use implicit type
diff --git a/test/Microsoft.ComponentDetection.Detectors.Tests/RustSbomDetectorTests.cs b/test/Microsoft.ComponentDetection.Detectors.Tests/RustSbomDetectorTests.cs
index 7e01e2cf6..63d72a4bc 100644
--- a/test/Microsoft.ComponentDetection.Detectors.Tests/RustSbomDetectorTests.cs
+++ b/test/Microsoft.ComponentDetection.Detectors.Tests/RustSbomDetectorTests.cs
@@ -206,6 +206,8 @@ public class RustSbomDetectorTests : BaseDetectorTest
public async Task TestGraphIsCorrectAsync()
{
var sbom = CargoSbom.FromJson(this.testSbom);
+ var lockfile = new LockfileLoader("C:\\Users\\nahammond\\source\\repos\\component-detection\\src\\CargoCLock\\Cargo.lock");
+ var mystring = lockfile.ToString();
var (result, componentRecorder) = await this.DetectorTestUtility
.WithFile("main.exe.cargo-sbom.json", this.testSbom)