.cargo/audit.toml

0 → 100644
+10 −0
Original line number Diff line number Diff line
# Project-level cargo-audit configuration
# See: https://github.com/RustSec/rustsec/blob/main/cargo-audit/audit.toml.example

[advisories]
# Ignored advisories with reasons:
# RUSTSEC-2025-0134: Transitive dependency 'rustls-pemfile' is marked
# unmaintained; accepted temporarily due to upstream dependency chain
# (hyper-rustls via aws-smithy). Re-evaluate and remove when aws-* deps
# update or the advisory is resolved upstream.
ignore = ["RUSTSEC-2025-0134"]
+1 −0
Original line number Diff line number Diff line
@@ -2932,6 +2932,7 @@ dependencies = [
 "http-body 1.0.1",
 "http-body-util",
 "md-5 0.11.0-rc.3",
 "reqwest",
 "s3s-test",
 "tracing",
]
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ bytes = "1.11.0"
http-body = "1.0.1"
md-5.workspace = true
base64-simd = "0.8.0"
reqwest = { version = "0.12.24", default-features = false, features = ["rustls-tls"] }

[dependencies.aws-config]
version = "1.8.7"
+88 −0
Original line number Diff line number Diff line
@@ -1292,6 +1292,94 @@ mod tests {
        assert_eq!(ans, "");
    }

    #[test]
    fn example_put_presigned_url() {
        // Test PUT presigned URL signing - similar to GET but with PUT method
        // This is used for uploading files to S3 using presigned URLs
        // Reference: https://docs.aws.amazon.com/AmazonS3/latest/userguide/PresignedUrlUploadObject.html
        let secret_access_key = SecretKey::from("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
        let method = Method::PUT;
        let headers = OrderedHeaders::from_slice_unchecked(&[("host", "examplebucket.s3.amazonaws.com")]);

        // Query strings for signing (without signature - signature is computed from these)
        let query_strings_for_signing = &[
            ("X-Amz-Algorithm", "AWS4-HMAC-SHA256"),
            ("X-Amz-Credential", "AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request"),
            ("X-Amz-Date", "20130524T000000Z"),
            ("X-Amz-Expires", "86400"),
            ("X-Amz-SignedHeaders", "host"),
        ];

        let canonical_request = create_presigned_canonical_request(&method, "/test.txt", query_strings_for_signing, &headers);

        // Canonical request for PUT should be similar to GET, just with PUT method
        assert_eq!(
            canonical_request,
            concat!(
                "PUT\n",
                "/test.txt\n",
                "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130524T000000Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host\n",
                "host:examplebucket.s3.amazonaws.com\n",
                "\n",
                "host\n",
                "UNSIGNED-PAYLOAD",
            )
        );

        let amz_date = AmzDate::parse("20130524T000000Z").unwrap();
        let string_to_sign = create_string_to_sign(&canonical_request, &amz_date, "us-east-1", "s3");
        let signature = calculate_signature(&string_to_sign, &secret_access_key, &amz_date, "us-east-1", "s3");

        // Signature value derived from the above test inputs (not from official AWS test vectors)
        assert_eq!(signature, "f4db56459304dafaa603a99a23c6bea8821890259a65c18ff503a4a72a80efd9");
    }

    #[test]
    fn example_put_presigned_url_with_content_type() {
        // Test PUT presigned URL with content-type signed header
        // When content-type is in signed headers, it must match the request header exactly
        let secret_access_key = SecretKey::from("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
        let method = Method::PUT;

        // Headers include content-type which is signed
        let headers = OrderedHeaders::from_slice_unchecked(&[
            ("content-type", "application/octet-stream"),
            ("host", "examplebucket.s3.amazonaws.com"),
        ]);

        let query_strings_for_signing = &[
            ("X-Amz-Algorithm", "AWS4-HMAC-SHA256"),
            ("X-Amz-Credential", "AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request"),
            ("X-Amz-Date", "20130524T000000Z"),
            ("X-Amz-Expires", "86400"),
            ("X-Amz-SignedHeaders", "content-type;host"),
        ];

        let canonical_request = create_presigned_canonical_request(&method, "/test.txt", query_strings_for_signing, &headers);

        // Canonical request should include content-type header
        assert_eq!(
            canonical_request,
            concat!(
                "PUT\n",
                "/test.txt\n",
                "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130524T000000Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=content-type%3Bhost\n",
                "content-type:application/octet-stream\n",
                "host:examplebucket.s3.amazonaws.com\n",
                "\n",
                "content-type;host\n",
                "UNSIGNED-PAYLOAD",
            )
        );

        let amz_date = AmzDate::parse("20130524T000000Z").unwrap();
        let string_to_sign = create_string_to_sign(&canonical_request, &amz_date, "us-east-1", "s3");
        let signature = calculate_signature(&string_to_sign, &secret_access_key, &amz_date, "us-east-1", "s3");

        // Signature value derived from the test inputs above; not from official AWS test vectors.
        assert_eq!(signature, "fd31b71961609f4b313497cb07ab0aedd268863bd547cc198db23cf04b8f663d");
    }

    #[test]
    fn multi_value_headers_combined_with_comma() {
        // Test that multiple headers with the same name are combined into a single line