From c271b2208d8838fd5950e0ad33b8e1351a51e954 Mon Sep 17 00:00:00 2001 From: texastony <5892063+texastony@users.noreply.github.com> Date: Wed, 14 Jan 2026 18:18:44 -0600 Subject: [PATCH 1/5] feat: Exceptions --- src/s3_encryption/exceptions.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/s3_encryption/exceptions.py b/src/s3_encryption/exceptions.py index 748075fc..1969d47b 100644 --- a/src/s3_encryption/exceptions.py +++ b/src/s3_encryption/exceptions.py @@ -5,6 +5,37 @@ This module contains custom exception classes used throughout the S3 Encryption Client. """ +# TODO: Data Key may be the wrong term. Materials MAY be a better term. +# To resolve, will look at the other S3 implementations. + +# TODO: Should this extend BotoCoreError? +# That way a customer can catch all AWS exceptions, +# regardless if it is Crypto Tools or something else. class S3EncryptionClientError(Exception): """Exception class for S3 Encryption Client errors.""" + + +class DecryptEncryptedDataKeyError(S3EncryptionClientError): + """The encrypted data key could not be decrypted.""" + + +# TODO: Technically, S3EC may never encrypt data keys, just generate them... +class EncryptDataKeyError(S3EncryptionClientError): + """The data key could not be encrypted.""" + + +class GenerateDataKeyError(S3EncryptionClientError): + """The data key could not be generated.""" + + +class CommitmentPolicyError(S3EncryptionClientError): + """The request or object does not comply with the commitment policy.""" + + +class CommitmentViolationError(S3EncryptionClientError): + """The object failed the Key Commitment check.""" + + +class AlgorithmSuiteNotSupportedError(S3EncryptionClientError): + """The request utilizes an unsupported or unkown algorithm suite.""" From 7e6e674b0c17acc9a15c47ddd6a65ba1496d8c73 Mon Sep 17 00:00:00 2001 From: texastony <5892063+texastony@users.noreply.github.com> Date: Sat, 24 Jan 2026 13:52:19 -0600 Subject: [PATCH 2/5] some duvet stuff we may not need --- .duvet/config.toml | 28 +++++++++++++++++++++++ .github/workflows/duvet.yml | 44 +++++++++++++++++++++++++++++++++++++ Makefile | 6 +++++ 3 files changed, 78 insertions(+) create mode 100644 .duvet/config.toml create mode 100644 .github/workflows/duvet.yml diff --git a/.duvet/config.toml b/.duvet/config.toml new file mode 100644 index 00000000..2a4f5d5b --- /dev/null +++ b/.duvet/config.toml @@ -0,0 +1,28 @@ +'$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json" + +[[source]] +pattern = "src/**/*.py" + +[[source]] +pattern = "test/**/*.py" +type = "test" # Sets the default annotation type + +# Include required specifications here +# TODO only specs with exceptions are included at this time. +[[specification]] +source = "specification/s3-encryption/client.md" +[[specification]] +source = "specification/s3-encryption/data-format/content-metadata.md" +[[specification]] +source = "specification/s3-encryption/decryption.md" +[[specification]] +source = "specification/s3-encryption/materials/s3-keyring.md" + +[report.html] +enabled = true +# Sets the path to the report output +path = ".duvet/reports/report.html" + +# Enable snapshots to prevent requirement coverage regressions +[report.snapshot] +enabled = false \ No newline at end of file diff --git a/.github/workflows/duvet.yml b/.github/workflows/duvet.yml new file mode 100644 index 00000000..3f415db9 --- /dev/null +++ b/.github/workflows/duvet.yml @@ -0,0 +1,44 @@ +name: Generate Duvet Report + +on: + workflow_call: + # Optional inputs that can be provided when calling this workflow + +jobs: + duvet: + runs-on: macos-latest + permissions: + id-token: write + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + + - name: Setup Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: stable + + - name: Clone duvet repository + run: git clone https://github.com/awslabs/duvet.git /tmp/duvet + + - name: Build and install duvet + run: | + cd /tmp/duvet + cargo xtask build + cargo install --path ./duvet + + - name: Run duvet + if: always() + run: make duvet + + - name: Upload duvet reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: duvetReports + include-hidden-files: true + path: duvet/reports/report.html diff --git a/Makefile b/Makefile index 814ab334..b56b8bd0 100644 --- a/Makefile +++ b/Makefile @@ -37,3 +37,9 @@ clean: find . -type d -name .pytest_cache -exec rm -rf {} + find . -type d -name .coverage -exec rm -rf {} + find . -type f -name "*.pyc" -delete + +duvet: + duvet report --config-path .duvet/config.toml + +view-report-mac: + open .duvet/reports/report.html From 5758f14fcf9d7ba3d19e4f5511f92f3f3f67fd4d Mon Sep 17 00:00:00 2001 From: texastony <5892063+texastony@users.noreply.github.com> Date: Sat, 24 Jan 2026 14:33:39 -0600 Subject: [PATCH 3/5] fix(exceptions): properly initialize BotoCoreError subclasses with message formatting Add BotoCoreError import and implement proper exception initialization: - Add fmt class attribute with {msg} placeholder for message formatting - Override __init__ to accept message parameter with sensible defaults - Add docstrings to __init__ methods - Add unit tests for default, custom exception messages, and inherentance. kiro-clie Cost: ~110,565 tokens Fixes syntax errors and TypeError when raising exceptions with messages. --- src/s3_encryption/exceptions.py | 39 +++++++++---------------- test/test_exceptions.py | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 26 deletions(-) create mode 100644 test/test_exceptions.py diff --git a/src/s3_encryption/exceptions.py b/src/s3_encryption/exceptions.py index 1969d47b..463a180d 100644 --- a/src/s3_encryption/exceptions.py +++ b/src/s3_encryption/exceptions.py @@ -5,37 +5,24 @@ This module contains custom exception classes used throughout the S3 Encryption Client. """ -# TODO: Data Key may be the wrong term. Materials MAY be a better term. -# To resolve, will look at the other S3 implementations. +from botocore.exceptions import BotoCoreError -# TODO: Should this extend BotoCoreError? -# That way a customer can catch all AWS exceptions, -# regardless if it is Crypto Tools or something else. -class S3EncryptionClientError(Exception): - """Exception class for S3 Encryption Client errors.""" +class S3EncryptionClientError(BotoCoreError): + """Exception class for non-Security S3 Encryption Client errors.""" + fmt = "{msg}" -class DecryptEncryptedDataKeyError(S3EncryptionClientError): - """The encrypted data key could not be decrypted.""" + def __init__(self, message="An unspecified S3 Encryption Client error occurred"): + """Initialize the exception with a message.""" + super().__init__(msg=message) -# TODO: Technically, S3EC may never encrypt data keys, just generate them... -class EncryptDataKeyError(S3EncryptionClientError): - """The data key could not be encrypted.""" +class S3EncryptionClientSecurityError(BotoCoreError): + """Security Exceptions for S3 Encryption Client errors.""" + fmt = "{msg}" -class GenerateDataKeyError(S3EncryptionClientError): - """The data key could not be generated.""" - - -class CommitmentPolicyError(S3EncryptionClientError): - """The request or object does not comply with the commitment policy.""" - - -class CommitmentViolationError(S3EncryptionClientError): - """The object failed the Key Commitment check.""" - - -class AlgorithmSuiteNotSupportedError(S3EncryptionClientError): - """The request utilizes an unsupported or unkown algorithm suite.""" + def __init__(self, message="An unspecified S3 Encryption Client Security error occurred"): + """Initialize the exception with a message.""" + super().__init__(msg=message) diff --git a/test/test_exceptions.py b/test/test_exceptions.py new file mode 100644 index 00000000..f93e3d9d --- /dev/null +++ b/test/test_exceptions.py @@ -0,0 +1,51 @@ +import pytest +from botocore.exceptions import BotoCoreError + +from s3_encryption.exceptions import ( + S3EncryptionClientError, + S3EncryptionClientSecurityError, +) + + +class TestS3EncryptionClientError: + def test_default_message(self): + error = S3EncryptionClientError() + assert str(error) == "An unspecified S3 Encryption Client error occurred" + + def test_custom_message(self): + error = S3EncryptionClientError("Custom error message") + assert str(error) == "Custom error message" + + def test_empty_message(self): + error = S3EncryptionClientError("") + assert str(error) == "" + + def test_inherits_from_botocore_error(self): + error = S3EncryptionClientError("test") + assert isinstance(error, BotoCoreError) + + def test_can_be_caught_as_botocore_error(self): + with pytest.raises(BotoCoreError): + raise S3EncryptionClientError("test error") + + +class TestS3EncryptionClientSecurityError: + def test_default_message(self): + error = S3EncryptionClientSecurityError() + assert str(error) == "An unspecified S3 Encryption Client Security error occurred" + + def test_custom_message(self): + error = S3EncryptionClientSecurityError("Custom security error") + assert str(error) == "Custom security error" + + def test_empty_message(self): + error = S3EncryptionClientSecurityError("") + assert str(error) == "" + + def test_inherits_from_botocore_error(self): + error = S3EncryptionClientSecurityError("test") + assert isinstance(error, BotoCoreError) + + def test_can_be_caught_as_botocore_error(self): + with pytest.raises(BotoCoreError): + raise S3EncryptionClientSecurityError("test security error") From fa8798c3c8cd4689a8d5117e163042e1182a170f Mon Sep 17 00:00:00 2001 From: texastony <5892063+texastony@users.noreply.github.com> Date: Sat, 24 Jan 2026 14:39:58 -0600 Subject: [PATCH 4/5] chore: remove incomplete duvet work --- .duvet/config.toml | 28 ----------------------- .github/workflows/duvet.yml | 44 ------------------------------------- Makefile | 6 ----- 3 files changed, 78 deletions(-) delete mode 100644 .duvet/config.toml delete mode 100644 .github/workflows/duvet.yml diff --git a/.duvet/config.toml b/.duvet/config.toml deleted file mode 100644 index 2a4f5d5b..00000000 --- a/.duvet/config.toml +++ /dev/null @@ -1,28 +0,0 @@ -'$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json" - -[[source]] -pattern = "src/**/*.py" - -[[source]] -pattern = "test/**/*.py" -type = "test" # Sets the default annotation type - -# Include required specifications here -# TODO only specs with exceptions are included at this time. -[[specification]] -source = "specification/s3-encryption/client.md" -[[specification]] -source = "specification/s3-encryption/data-format/content-metadata.md" -[[specification]] -source = "specification/s3-encryption/decryption.md" -[[specification]] -source = "specification/s3-encryption/materials/s3-keyring.md" - -[report.html] -enabled = true -# Sets the path to the report output -path = ".duvet/reports/report.html" - -# Enable snapshots to prevent requirement coverage regressions -[report.snapshot] -enabled = false \ No newline at end of file diff --git a/.github/workflows/duvet.yml b/.github/workflows/duvet.yml deleted file mode 100644 index 3f415db9..00000000 --- a/.github/workflows/duvet.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Generate Duvet Report - -on: - workflow_call: - # Optional inputs that can be provided when calling this workflow - -jobs: - duvet: - runs-on: macos-latest - permissions: - id-token: write - contents: read - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: true - - - name: Setup Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - - - name: Clone duvet repository - run: git clone https://github.com/awslabs/duvet.git /tmp/duvet - - - name: Build and install duvet - run: | - cd /tmp/duvet - cargo xtask build - cargo install --path ./duvet - - - name: Run duvet - if: always() - run: make duvet - - - name: Upload duvet reports - if: always() - uses: actions/upload-artifact@v4 - with: - name: duvetReports - include-hidden-files: true - path: duvet/reports/report.html diff --git a/Makefile b/Makefile index b56b8bd0..814ab334 100644 --- a/Makefile +++ b/Makefile @@ -37,9 +37,3 @@ clean: find . -type d -name .pytest_cache -exec rm -rf {} + find . -type d -name .coverage -exec rm -rf {} + find . -type f -name "*.pyc" -delete - -duvet: - duvet report --config-path .duvet/config.toml - -view-report-mac: - open .duvet/reports/report.html From f1202c63f44143b8e39f024f0929779804662b6e Mon Sep 17 00:00:00 2001 From: texastony <5892063+texastony@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:25:09 -0600 Subject: [PATCH 5/5] Trigger CI build