ebicsclient 1.3.1__tar.gz → 1.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/PKG-INFO +12 -1
  2. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/README.md +11 -0
  3. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/06-engineering-conventions.md +8 -0
  4. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/pyproject.toml +1 -1
  5. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/models.py +1 -1
  6. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_input_validation.py +4 -4
  7. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/uv.lock +1 -1
  8. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/.github/workflows/ci.yml +0 -0
  9. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/.github/workflows/release.yml +0 -0
  10. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/.gitignore +0 -0
  11. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/CLAUDE.md +0 -0
  12. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/CONTRIBUTING.md +0 -0
  13. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/LICENSE.md +0 -0
  14. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/01-protocol-and-formats.md +0 -0
  15. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/02-licensing-strategy.md +0 -0
  16. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/03-library-landscape.md +0 -0
  17. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/04-implementation-plan.md +0 -0
  18. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/05-zkb-onboarding.md +0 -0
  19. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/07-handshake-testing.md +0 -0
  20. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/08-parity-and-xsd-findings.md +0 -0
  21. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/09-zkb-test-platform-settings.md +0 -0
  22. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/10-btf-order-types.md +0 -0
  23. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/docs/11-certificate-profiles.md +0 -0
  24. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/examples/zkb_handshake.py +0 -0
  25. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/__init__.py +0 -0
  26. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/certificates.py +0 -0
  27. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/client.py +0 -0
  28. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/crypto.py +0 -0
  29. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/errors.py +0 -0
  30. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/formats/__init__.py +0 -0
  31. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/formats/camt.py +0 -0
  32. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/formats/camt052.py +0 -0
  33. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/formats/camt053.py +0 -0
  34. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/formats/camt054.py +0 -0
  35. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/formats/container.py +0 -0
  36. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/formats/pain002.py +0 -0
  37. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/keys.py +0 -0
  38. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/letter.py +0 -0
  39. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/protocol/__init__.py +0 -0
  40. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/protocol/h005.py +0 -0
  41. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/py.typed +0 -0
  42. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/src/ebicsclient/transport.py +0 -0
  43. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/crypto_helpers.py +0 -0
  44. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/data/camt052_zkb_sample.xml +0 -0
  45. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/data/camt053_zkb_sample.xml +0 -0
  46. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/data/camt054_collective_zkb_sample.xml +0 -0
  47. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/data/camt054_credit_zkb_sample.xml +0 -0
  48. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/data/camt054_debit_zkb_sample.xml +0 -0
  49. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/data/htd_zkb_sample.xml +0 -0
  50. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/data/pain002_accp.xml +0 -0
  51. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/data/pain002_actc.xml +0 -0
  52. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/data/pain002_part.xml +0 -0
  53. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/data/pain002_rjct.xml +0 -0
  54. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/fixtures/parity/hia.xml +0 -0
  55. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/fixtures/parity/hpb.xml +0 -0
  56. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/fixtures/parity/ini.xml +0 -0
  57. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/fixtures/parity/meta.json +0 -0
  58. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_c14n_vectors.py +0 -0
  59. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_camt052_054.py +0 -0
  60. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_camt053.py +0 -0
  61. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_certificates.py +0 -0
  62. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_client.py +0 -0
  63. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_crypto.py +0 -0
  64. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_errors.py +0 -0
  65. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_h005.py +0 -0
  66. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_keys.py +0 -0
  67. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_letter.py +0 -0
  68. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_pain002.py +0 -0
  69. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_php_parity.py +0 -0
  70. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_schema_validation.py +0 -0
  71. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tests/test_transport.py +0 -0
  72. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tools/fetch-schemas.py +0 -0
  73. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tools/php-parity/README.md +0 -0
  74. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tools/php-parity/composer.json +0 -0
  75. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tools/php-parity/composer.lock +0 -0
  76. {ebicsclient-1.3.1 → ebicsclient-1.3.2}/tools/php-parity/generate.php +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ebicsclient
3
- Version: 1.3.1
3
+ Version: 1.3.2
4
4
  Summary: Pure-Python client for the EBICS 3.0 (H005) banking protocol
5
5
  Project-URL: Homepage, https://github.com/schulluk/ebicsclient
6
6
  Project-URL: Repository, https://github.com/schulluk/ebicsclient
@@ -189,6 +189,17 @@ transaction_id = client.upload(PAIN_001, pain001_bytes)
189
189
  The **certificate-based ("mit Zertifikaten")** profile is a constructor option — see
190
190
  [docs/11-certificate-profiles.md](docs/11-certificate-profiles.md).
191
191
 
192
+ > ### ⚠️ Loading EBICS identifiers from a config file? Quote them.
193
+ >
194
+ > EBICS IDs **can** carry leading zeros (a real Partner ID may look like `00123456`), and
195
+ > ISO message versions look like `"08"`. Unquoted in YAML/JSON/TOML these parse as **numbers** —
196
+ > the wrong type *and* silently stripped of their zeros (PyYAML even reads all-octal-digit
197
+ > values as octal). Since 1.3.1 the library rejects non-string values immediately with an
198
+ > explanatory error, but the correct fix is always **quoting the value in your config**
199
+ > (`partner_id: "00123456"`, `message_version: "08"`) — never wrapping the parsed number in
200
+ > `str()`, which would keep the wrong, zero-stripped identifier and talk to the bank as the
201
+ > wrong subscriber. The same applies to digits-only keyring passphrases.
202
+
192
203
  ## Documentation index
193
204
 
194
205
  | Doc | Contents |
@@ -71,6 +71,17 @@ transaction_id = client.upload(PAIN_001, pain001_bytes)
71
71
  The **certificate-based ("mit Zertifikaten")** profile is a constructor option — see
72
72
  [docs/11-certificate-profiles.md](docs/11-certificate-profiles.md).
73
73
 
74
+ > ### ⚠️ Loading EBICS identifiers from a config file? Quote them.
75
+ >
76
+ > EBICS IDs **can** carry leading zeros (a real Partner ID may look like `00123456`), and
77
+ > ISO message versions look like `"08"`. Unquoted in YAML/JSON/TOML these parse as **numbers** —
78
+ > the wrong type *and* silently stripped of their zeros (PyYAML even reads all-octal-digit
79
+ > values as octal). Since 1.3.1 the library rejects non-string values immediately with an
80
+ > explanatory error, but the correct fix is always **quoting the value in your config**
81
+ > (`partner_id: "00123456"`, `message_version: "08"`) — never wrapping the parsed number in
82
+ > `str()`, which would keep the wrong, zero-stripped identifier and talk to the bank as the
83
+ > wrong subscriber. The same applies to digits-only keyring passphrases.
84
+
74
85
  ## Documentation index
75
86
 
76
87
  | Doc | Contents |
@@ -91,6 +91,14 @@ specific return code on `ReturnCodeError`).
91
91
  - **Gathering** those values from env vars / a vault / Django settings is the **consumer's** job, never the
92
92
  library's. Explicit config is auditable; silent environment reads are a surprise and a leakage footgun on a
93
93
  money-moving codebase.
94
+ - **Validate at the public boundary, with errors that teach the fix.** Every caller-supplied value is
95
+ type-checked where it enters the library (dataclass `__post_init__`, first line of a public function) —
96
+ never allowed to drift into `lxml`/`cryptography` and die there as a cryptic C-extension error. Learned
97
+ from the field: EBICS identifiers **can** carry leading zeros (`"00123456"`) and versions look like `"08"`;
98
+ unquoted in a consumer's YAML/JSON config they arrive as *numbers* — wrong type **and** silently
99
+ zero-stripped. The error message must therefore say "quote the value in your configuration", because the
100
+ obvious caller "fix" (`str(...)`) would keep a wrong, zero-stripped identifier and address the wrong
101
+ subscriber. See the README's quoting warning and `tests/test_input_validation.py`.
94
102
 
95
103
  ## Security (banking client — non-negotiable)
96
104
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ebicsclient"
7
- version = "1.3.1"
7
+ version = "1.3.2"
8
8
  description = "Pure-Python client for the EBICS 3.0 (H005) banking protocol"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -13,7 +13,7 @@ from cryptography.hazmat.primitives.asymmetric import rsa
13
13
 
14
14
 
15
15
  def _require_identifier(field_name: str, value: object) -> None:
16
- # EBICS identifiers frequently carry leading zeros (e.g. a Partner ID of "00705443").
16
+ # EBICS identifiers can carry leading zeros (e.g. a Partner ID of "00123456").
17
17
  # Loaded from an unquoted YAML/JSON/TOML config value they arrive as numbers — the wrong
18
18
  # type AND silently stripped of their zeros (PyYAML even reads all-octal-digit values as
19
19
  # octal). Failing here, with the fix in the message, beats a cryptic TypeError four
@@ -5,7 +5,7 @@ value from an unquoted YAML/JSON config entry) slipping deep into a C extension
5
5
  surfacing as a cryptic error — as happened live with a numeric ``user_id`` reaching
6
6
  ``x509.NameAttribute`` (``TypeError: value argument must be a str``). Every boundary must
7
7
  fail immediately, with a message that teaches the fix. EBICS identifiers with leading
8
- zeros (``"00705443"``) also *lose their zeros* when parsed as numbers, so the messages
8
+ zeros (``"00123456"``) also *lose their zeros* when parsed as numbers, so the messages
9
9
  point at quoting the config value, never at wrapping it in ``str()``.
10
10
  """
11
11
 
@@ -40,9 +40,9 @@ def keyring() -> Keyring:
40
40
 
41
41
 
42
42
  def test_user_rejects_a_numeric_id_with_a_teaching_message() -> None:
43
- # The exact bug report: ZKB IDs like "00705443" unquoted in config arrive as ints.
43
+ # The exact bug report: ZKB IDs like "00123456" unquoted in config arrive as ints.
44
44
  with pytest.raises(TypeError) as caught:
45
- User(partner_id="00705443", user_id=705443) # type: ignore[arg-type]
45
+ User(partner_id="00123456", user_id=123456) # type: ignore[arg-type]
46
46
  message = str(caught.value)
47
47
  assert "user_id must be a str" in message
48
48
  assert "int" in message
@@ -173,6 +173,6 @@ def test_the_original_bug_now_fails_at_construction_not_in_cryptography() -> Non
173
173
  # Regression for the live report: with a numeric user_id the failure must happen at
174
174
  # User(...) with our message — never reach x509.NameAttribute inside cryptography.
175
175
  with pytest.raises(TypeError) as caught:
176
- User(partner_id=705443, user_id=705443) # type: ignore[arg-type]
176
+ User(partner_id=123456, user_id=123456) # type: ignore[arg-type]
177
177
  assert "value argument must be a str" not in str(caught.value)
178
178
  assert "leading zeros" in str(caught.value)
@@ -281,7 +281,7 @@ wheels = [
281
281
 
282
282
  [[package]]
283
283
  name = "ebicsclient"
284
- version = "1.3.1"
284
+ version = "1.3.2"
285
285
  source = { editable = "." }
286
286
  dependencies = [
287
287
  { name = "cryptography" },
File without changes
File without changes
File without changes
File without changes