ksef-client-rails 0.1.0 → 1.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +161 -0
- data/NOTICE +40 -0
- data/README.md +524 -6
- data/examples/fa3_full.xml +143 -0
- data/examples/fa3_minimal.xml +65 -0
- data/examples/fa3_typical.xml +76 -0
- data/lib/generators/ksef/install/install_generator.rb +20 -0
- data/lib/generators/ksef/install/templates/ksef.rb +25 -0
- data/lib/ksef/auth/coordinator.rb +164 -0
- data/lib/ksef/auth/ksef_token_encryptor.rb +36 -0
- data/lib/ksef/batch/package_builder.rb +125 -0
- data/lib/ksef/batch/splitter.rb +99 -0
- data/lib/ksef/batch/uploader.rb +114 -0
- data/lib/ksef/client.rb +135 -0
- data/lib/ksef/configuration.rb +206 -0
- data/lib/ksef/crypto/cryptography_service.rb +136 -0
- data/lib/ksef/crypto/csr.rb +106 -0
- data/lib/ksef/crypto/public_keys.rb +125 -0
- data/lib/ksef/environments.rb +61 -0
- data/lib/ksef/errors.rb +187 -0
- data/lib/ksef/http/connection.rb +432 -0
- data/lib/ksef/http/error_parser.rb +194 -0
- data/lib/ksef/http/pagination.rb +80 -0
- data/lib/ksef/invoices/builder/checks.rb +127 -0
- data/lib/ksef/invoices/builder/number.rb +67 -0
- data/lib/ksef/invoices/builder/xml.rb +256 -0
- data/lib/ksef/invoices/builder.rb +251 -0
- data/lib/ksef/invoices/export.rb +241 -0
- data/lib/ksef/invoices/sample.rb +78 -0
- data/lib/ksef/invoices/schema_outline.rb +120 -0
- data/lib/ksef/invoices/validator.rb +200 -0
- data/lib/ksef/models/form_code.rb +49 -0
- data/lib/ksef/models/ksef_number.rb +74 -0
- data/lib/ksef/models/nip.rb +62 -0
- data/lib/ksef/polish_time.rb +49 -0
- data/lib/ksef/polling.rb +65 -0
- data/lib/ksef/qr/code.rb +104 -0
- data/lib/ksef/qr/verification_link.rb +215 -0
- data/lib/ksef/railtie.rb +20 -0
- data/lib/ksef/resources/auth.rb +116 -0
- data/lib/ksef/resources/base.rb +120 -0
- data/lib/ksef/resources/batch_sessions.rb +43 -0
- data/lib/ksef/resources/certificates.rb +82 -0
- data/lib/ksef/resources/collective_identifiers.rb +70 -0
- data/lib/ksef/resources/exports.rb +47 -0
- data/lib/ksef/resources/invoices.rb +67 -0
- data/lib/ksef/resources/limits.rb +33 -0
- data/lib/ksef/resources/online_sessions.rb +62 -0
- data/lib/ksef/resources/peppol.rb +21 -0
- data/lib/ksef/resources/permissions.rb +180 -0
- data/lib/ksef/resources/security.rb +18 -0
- data/lib/ksef/resources/sessions.rb +125 -0
- data/lib/ksef/resources/test_data.rb +178 -0
- data/lib/ksef/resources/tokens.rb +62 -0
- data/lib/ksef/sessions/online.rb +168 -0
- data/lib/ksef/sync/incremental.rb +169 -0
- data/lib/ksef/testing.rb +153 -0
- data/lib/ksef/token_manager.rb +130 -0
- data/lib/ksef/version.rb +11 -0
- data/lib/ksef/xades/auth_token_request.rb +135 -0
- data/lib/ksef/xades/external_signer.rb +58 -0
- data/lib/ksef/xades/signer.rb +312 -0
- data/lib/ksef-client-rails.rb +3 -2
- data/lib/ksef.rb +116 -0
- data/schemas/FA/schemat_FA(2)_v1-0E.xsd +3661 -0
- data/schemas/FA/schemat_FA(3)_v1-0E.xsd +3950 -0
- data/schemas/README.md +53 -0
- data/schemas/RR/schemat_FA_RR(1)_v1-1E.xsd +2188 -0
- data/schemas/bazowe/ElementarneTypyDanych_v10-0E.xsd +1 -0
- data/schemas/bazowe/KodyKrajow_v10-0E.xsd +1283 -0
- data/schemas/bazowe/StrukturyDanych_v10-0E.xsd +1 -0
- metadata +291 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ad5bc199c88428b14246234faed79b641060f5a87b4daa6157cfbc33a82c67d
|
|
4
|
+
data.tar.gz: 2f4efe72191d1a0cfa1093856a37c9cda4d4b821ddb94bdbb36562197a352b86
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 274542ba6918c449c35a3d758c660fbd1c178a2c1a93e73bc244ccbc719495f6430d432ba6f704ef27cb76af447ace44aa9b00ea069419ff1f320a03d5aa52cd
|
|
7
|
+
data.tar.gz: eb7a19017a260b466230d8bcec04cfb4280824e9a67558e8f7cbdead5ee2781df369ba94b701f02eafa76d017b87802cbf78686e0e719537ae12f2e8db3f6892
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
4
|
+
The gem tracks the KSeF API contract version (currently **2.8.1**, `Ksef::API_VERSION`); every
|
|
5
|
+
request is checked against that version's OpenAPI document in the test suite.
|
|
6
|
+
|
|
7
|
+
## [1.0.0] — 2026-10-14
|
|
8
|
+
|
|
9
|
+
First functional release: a pure-Ruby client for KSeF API 2.0, covering every operation of the
|
|
10
|
+
2.8.1 contract plus high-level flows. Rails integration is optional.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Client and configuration:** `Ksef::Client` facade with 14 domain resources mapped 1:1 to the
|
|
14
|
+
API; global or per-client `Configuration` (`#apply` assigns any option and rejects unknown keys,
|
|
15
|
+
`#with` copies it with some options changed — e.g. another environment);
|
|
16
|
+
TEST, DEMO and PROD environments; option values are type-checked when set, and a client copies the
|
|
17
|
+
configuration it is built with. The access token may be a callable read for every request attempt,
|
|
18
|
+
`with_access_token` derives a per-context client that shares the connection and the key cache, and
|
|
19
|
+
`close` closes the keep-alive connections. One CA store per process.
|
|
20
|
+
- **HTTP layer:** retries — a `429` for any call, honouring `Retry-After` up to
|
|
21
|
+
`config.max_retry_after` (60 s by default, `nil` for no limit; a longer wait is raised as
|
|
22
|
+
`Ksef::RateLimited`), and `500`/`502`/`503`/`504` or connection failures for idempotent requests
|
|
23
|
+
(GET, presigned transfers) with exponential backoff — also a GET whose reused TLS connection was
|
|
24
|
+
dropped without close_notify, and any request that never left the process (a connect or a
|
|
25
|
+
connection-pool timeout); transport failures — including the ones Faraday leaves unwrapped, and a
|
|
26
|
+
response cut short of its Content-Length — surface as `Ksef::NetworkError` /
|
|
27
|
+
`Ksef::RequestTimeout`. Keep-alive by
|
|
28
|
+
default (`faraday-net_http_persistent`; `config.adapter` picks another Faraday adapter), array query
|
|
29
|
+
parameters as repeated keys, presigned transfers for batch and export (normalized `https://host` URLs
|
|
30
|
+
only), both pagination schemes as lazy enumerators guarded against a server that never ends them (a
|
|
31
|
+
repeated token, a run of empty pages), polling with backoff, typed errors parsed from both API error
|
|
32
|
+
formats (including the legacy 429 body, the `reasonCode` of a 403 and each problem-details error's
|
|
33
|
+
description). A 2xx body that is not what the endpoint returns — not JSON, not a JSON object, without
|
|
34
|
+
a field the gem needs, or for an invoice or UPO download not an XML document or not matching its
|
|
35
|
+
`x-ms-meta-hash` — raises `Ksef::ApiError` instead of reading as an empty result; a JSON-typed 2xx
|
|
36
|
+
body is never quoted in it (it may carry a token), a non-JSON error body is kept as a short UTF-8
|
|
37
|
+
excerpt. A token with a control character is refused without being echoed. Every HTTP attempt is
|
|
38
|
+
logged at `debug` and published as `request.ksef` through ActiveSupport::Notifications (method, path
|
|
39
|
+
without the query, status).
|
|
40
|
+
- **Authentication:** KSeF token (RSA-OAEP) and XAdES-BASELINE-B signature (RSA of at least 2048 bits,
|
|
41
|
+
or EC of at least 256) with a replaceable signer (`#sign(xml)`/`#call(xml)` — HSM, qualified signature
|
|
42
|
+
services; checked, with the context, subject type and allowed IPs, before the challenge is
|
|
43
|
+
requested; its result must be the signed XML), token redemption and refresh, authentication
|
|
44
|
+
sessions, thread-safe `TokenManager` (refreshing at `validUntil`, or at the token's JWT `exp` claim
|
|
45
|
+
when that is not given; `on_refresh`, run outside the lock and again after a failure, to persist each
|
|
46
|
+
new token; `#access_token_valid_until`; a skew longer than the token's lifetime does not refresh on
|
|
47
|
+
every request). A NIP context is sent as its 10 digits, and an invalid one is refused before any
|
|
48
|
+
request. The signer refuses malformed XML, a DOCTYPE, a key without its private part and a
|
|
49
|
+
certificate it does not match; `X509IssuerName` decodes every string type and multi-valued RDNs.
|
|
50
|
+
- **Cryptography:** AES-256-CBC session encryption, RSA-OAEP with SHA-256/MGF1-SHA-256, Ministry
|
|
51
|
+
public-key cache (RSA certificates only) with a refetch when the cached list has no valid
|
|
52
|
+
certificate, and a retry on key
|
|
53
|
+
rotation (`21470`) in every flow that wraps a key — online and batch sessions, export (the same
|
|
54
|
+
key re-wrapped under the new certificate) and token authentication (a fresh challenge); PKCS#10
|
|
55
|
+
CSR generation (RSA/EC).
|
|
56
|
+
- **Invoices:** interactive session (open → encrypt → send → close → UPO; `valid_until`, a BOM or
|
|
57
|
+
non-UTF-8 invoice refused, a technical correction sent in offline mode, a rejection raised as
|
|
58
|
+
`Ksef::InvoiceRejected`); batch session (ZIP or
|
|
59
|
+
tar.gz — `compression: :zip | :tar_gz` — declared as `batchFile.compressionType`, split into parts
|
|
60
|
+
encrypted one at a time, the contract limits of 50 parts, 5 GB and 100 MB per part checked before
|
|
61
|
+
anything is sent — and so are an empty package, colliding or unsafe names and a tar entry name over
|
|
62
|
+
100 bytes —, UTF-8 ZIP entry names flagged as such, `Ksef::BatchUploadError` naming the session
|
|
63
|
+
when an upload or the close fails); invoice download; metadata queries (250 records per page by
|
|
64
|
+
default, `Ksef::ResultTruncated` at the 10,000-record limit, with the `dateRange` bound to move for
|
|
65
|
+
the sort order); package export (ZIP or tar.gz, checked against `package.compressionType`, streamed
|
|
66
|
+
with `each_entry` — names never absolute or climbing, each file capped at 100 MB unpacked —,
|
|
67
|
+
`_metadata.json` parsed by `metadata`, every part checked against its required `encryptedPartHash`);
|
|
68
|
+
incremental HWM sync (invoices paired with their metadata, a stop once the HWM stops moving, closed
|
|
69
|
+
90-day windows for a start more than 90 days back, nothing yielded while the HWM has not reached the
|
|
70
|
+
start, a `Date` start at Polish midnight, `#hwm` to persist between runs — after an error too).
|
|
71
|
+
Invoices and UPOs are returned as UTF-8 strings.
|
|
72
|
+
- **Offline validation:** `Ksef::Invoices::Validator` against the packaged FA(2), FA(3) and
|
|
73
|
+
FA_RR(1) schemas plus KSeF's XML rules (UTF-8 without a BOM, XML 1.0, no processing instructions,
|
|
74
|
+
none of the characters KSeF refuses), without a network call; the messages are libxml2's. The
|
|
75
|
+
XML is parsed strictly (a malformed or namespace-malformed document is reported, never repaired),
|
|
76
|
+
and each schema is parsed once per process. `#errors` gives each violation's element path and line,
|
|
77
|
+
`#outline` a form's field tree with the occurrence of enclosing groups and extension bases (and each
|
|
78
|
+
field's description from the schema on request); `FormCode#namespace` is the invoice namespace.
|
|
79
|
+
- **Invoice builder:** `Ksef::Invoices::Builder` writes FA(3) XML — parties (a buyer by NIP, EU VAT
|
|
80
|
+
number, another tax ID or none), lines with the ten rates and their per-rate summary (rounded half
|
|
81
|
+
up), payment, annotations (reverse charge set by an `oo` or `np II` line), corrections (lines as
|
|
82
|
+
differences), footer and registers. Amounts are parsed strictly and written exactly (prices and
|
|
83
|
+
discounts to 2 decimal places, quantities to 6); missing values, unknown codes, a date that is not a
|
|
84
|
+
Date or "YYYY-MM-DD", text KSeF refuses, a foreign-currency invoice with VAT and a `zw` line without
|
|
85
|
+
its legal basis are refused, and the output is checked against the schema.
|
|
86
|
+
`Ksef::Invoices::Sample.minimal`, `.typical` and `.full` are three invoices accepted on KSeF TEST,
|
|
87
|
+
dated with the Polish calendar day by default (`Ksef::PolishTime.today`) and packaged as
|
|
88
|
+
`examples/fa3_{minimal,typical,full}.xml`.
|
|
89
|
+
- **QR codes:** CODE I and CODE II links (RSASSA-PSS / ECDSA P-256 signatures with an RSA key of at
|
|
90
|
+
least 2048 bits, every path segment validated — the context type and value against the contract,
|
|
91
|
+
`NipVatUe` values with `+`/`*` included, a 16-hex-digit certificate serial (or `certificate.serial`),
|
|
92
|
+
a seller NIP normalized and checked, the CODE I date read from the invoice's `P_1`, a calendar date
|
|
93
|
+
and a `Time` refused — and KSeF's Base64 `invoiceHash` decoded and checked against the XML) and
|
|
94
|
+
rendering as scalable SVG with the required quiet zone and the label KSeF prescribes (the colour
|
|
95
|
+
validated), PNG or an ANSI terminal preview (optional `rqrcode` gem).
|
|
96
|
+
- **Other resources:** collective identifiers (API 2.7+), KSeF tokens, KSeF certificates,
|
|
97
|
+
permissions, limits, security, Peppol, TEST data (refused locally on PROD; includes
|
|
98
|
+
`update_certificate`). `sessions.list` requires the `session_type` the API requires and takes its
|
|
99
|
+
optional filters.
|
|
100
|
+
- **Value objects:** `Nip` and `KsefNumber` checked against the contract patterns plus the mod-11
|
|
101
|
+
checksum and CRC-8 (a NIP's separators — no-break spaces and dashes too — and `PL` prefix stripped;
|
|
102
|
+
`Nip.normalize!`; equal only to their own kind), `FormCode`; `Ksef::PolishTime` for the Polish
|
|
103
|
+
calendar day without tzinfo.
|
|
104
|
+
- **Rails:** `Ksef::Railtie` (`config.ksef.*` for every option, applied before the app's
|
|
105
|
+
initializers) and the `ksef:install` generator, whose
|
|
106
|
+
initializer sets only the environment — from `KSEF_ENV` when set and not empty, never from
|
|
107
|
+
`Rails.env` — and
|
|
108
|
+
leaves the rest commented out, so it does not override `config.ksef.*`.
|
|
109
|
+
- **Errors:** one hierarchy under `Ksef::Error`, including `Ksef::SessionUnavailable` for code
|
|
110
|
+
`21184` (a session under maintenance), `Ksef::ReauthenticationRequired` for `21301`/`21304` and a
|
|
111
|
+
401 from the token refresh (authenticate again), `Ksef::InvoiceRejected` and `Ksef::ResultTruncated`.
|
|
112
|
+
- **Testing:** `require "ksef/testing"` — WebMock stubs of the public keys (a certificate valid
|
|
113
|
+
2000–2099, so time travel in tests is safe), sign-in (token and XAdES), refresh and an interactive
|
|
114
|
+
session, with responses checked against the contract.
|
|
115
|
+
- **Secret hygiene:** `Ksef::Client`, `Ksef::TokenManager`, the authentication result, session
|
|
116
|
+
encryption data and a generated CSR redact tokens and keys in `#inspect` and in `pp` (the Rails
|
|
117
|
+
console), and an export result or a batch package shows only its size; `Configuration` and the
|
|
118
|
+
connection redact proxy credentials in every form Faraday accepts.
|
|
119
|
+
- **Verification tooling:** the contract suite against the 2.8.1 OpenAPI document
|
|
120
|
+
(`spec/fixtures/openapi/`), `rake contract:drift` comparing it with the live servers, and
|
|
121
|
+
`rake e2e:test` running every flow on live KSeF TEST without credentials.
|
|
122
|
+
- **Dependencies:** Ruby 3.3 or later; `faraday ~> 2.5`, `faraday-net_http_persistent ~> 2.3`
|
|
123
|
+
(`>= 2.3.1`) with `connection_pool >= 2.4` (the after-fork reset of the keep-alive pool),
|
|
124
|
+
`nokogiri >= 1.19.1, < 2` (the libxml2 security fixes), `rubyzip ~> 3.4` (older releases carry
|
|
125
|
+
CVE-2026-85396), `bigdecimal`, `base64`, `logger`; `rqrcode` and `minitar` are optional.
|
|
126
|
+
|
|
127
|
+
### Verified
|
|
128
|
+
- Offline RSpec suite on Ruby 3.3, 3.4 and 4.0 and at the lowest dependency versions the gemspec
|
|
129
|
+
allows, including the contract check of every request against the KSeF API 2.8.1 OpenAPI document
|
|
130
|
+
(all 83 operations, every parameter and every list's pagination); the built gem loads with its
|
|
131
|
+
runtime dependencies only; cryptography, the KSeF-token plaintext, CSRs and XAdES output (with
|
|
132
|
+
`SigningCertificate`) checked by an independent Python implementation.
|
|
133
|
+
- PROD publishes the 2.8.1 contract (`rake contract:drift`, 2026-10-14); TEST and DEMO publish 2.9.0.
|
|
134
|
+
- On the live KSeF TEST environment (2026-10-14, `rake e2e:test`, every flow passed; TEST already runs
|
|
135
|
+
API 2.9.0): XAdES authentication with RSA and EC seals, KSeF-token authentication, token refresh,
|
|
136
|
+
authentication sessions, limits, KSeF tokens (with the status filter), interactive session with UPO
|
|
137
|
+
(the three sample invoices included), batch sessions (ZIP in several parts and tar.gz) with UPO and
|
|
138
|
+
every invoice accepted, invoice download checked against its hash, metadata query, export (ZIP and
|
|
139
|
+
tar.gz), `Sync::Incremental` (also from 91 days back: a closed 90-day window, then an open one),
|
|
140
|
+
collective identifiers, Offline certificate enrollment with a CODE II link, permissions, Peppol,
|
|
141
|
+
TEST data, revocations.
|
|
142
|
+
- On the live KSeF DEMO environment through the demo application (2026-10-12): authentication (token
|
|
143
|
+
and XAdES), interactive and batch sessions with UPO, export, `Sync::Incremental`, permissions,
|
|
144
|
+
certificate enrollment and offline QR; on the live KSeF PROD environment (2026-10-12):
|
|
145
|
+
authentication with a KSeF token. A GET was retried live after a dropped keep-alive connection, and
|
|
146
|
+
a 429 slept through and retried.
|
|
147
|
+
- The Railtie and the `ksef:install` generator smoke-tested in a Rails 8.1 application.
|
|
148
|
+
|
|
149
|
+
### Known limitations
|
|
150
|
+
- Not exercised live: the 5xx retries, `SessionUnavailable` and a token refresh refused with 401.
|
|
151
|
+
The Railtie and the generator are not exercised by the demo application.
|
|
152
|
+
- No HTML/PDF invoice rendering, no PEF (UBL) schema validation. The builder writes VAT and KOR
|
|
153
|
+
invoices only (no advance, settlement or simplified invoices, no `Podmiot3`, no attachments), in PLN
|
|
154
|
+
when a line carries VAT, and a correction as difference lines (no `StanPrzed`).
|
|
155
|
+
- Batch and export packages are held in memory (one copy of the package).
|
|
156
|
+
- With the default keep-alive adapter, a proxy password containing percent-encoded characters fails
|
|
157
|
+
(`URI::InvalidComponentError`, faraday-net_http_persistent 2.3); use `adapter: :net_http`.
|
|
158
|
+
|
|
159
|
+
## [0.1.0] — 2026-09-24
|
|
160
|
+
|
|
161
|
+
Name reservation only; no functionality.
|
data/NOTICE
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Third-party material included in ksef-client-rails
|
|
2
|
+
==================================================
|
|
3
|
+
|
|
4
|
+
The XSD schemas in schemas/ (FA(2), FA(3), FA_RR(1) and the base types in schemas/bazowe/)
|
|
5
|
+
are copied from the official KSeF documentation repository published by the Polish Ministry of
|
|
6
|
+
Finance (https://github.com/CIRFMF/ksef-api); the forms are published on https://crd.gov.pl/. The
|
|
7
|
+
only change is the schemaLocation of the base-types import (see schemas/README.md). The OpenAPI document in
|
|
8
|
+
spec/fixtures/openapi/ (used by the test suite only, not packaged into the gem) is the KSeF API 2.8.1
|
|
9
|
+
document as the KSeF TEST server publishes it, unchanged; it has the same operations and schemas as
|
|
10
|
+
the one in that repository. Parts of the gem follow
|
|
11
|
+
the behaviour of the official KSeF client libraries (https://github.com/CIRFMF/ksef-client-csharp,
|
|
12
|
+
https://github.com/CIRFMF/ksef-client-java), which are published under the same licence.
|
|
13
|
+
|
|
14
|
+
This project is independent and is not affiliated with or endorsed by the Ministry of Finance.
|
|
15
|
+
|
|
16
|
+
That material is distributed under the following licence:
|
|
17
|
+
|
|
18
|
+
--------------------------------------------------------------------------------
|
|
19
|
+
MIT License
|
|
20
|
+
|
|
21
|
+
Copyright (c) 2025 Ministerstwo Finansów
|
|
22
|
+
|
|
23
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
24
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
25
|
+
in the Software without restriction, including without limitation the rights
|
|
26
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
27
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
28
|
+
furnished to do so, subject to the following conditions:
|
|
29
|
+
|
|
30
|
+
The above copyright notice and this permission notice shall be included in all
|
|
31
|
+
copies or substantial portions of the Software.
|
|
32
|
+
|
|
33
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
34
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
35
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
36
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
37
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
38
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
39
|
+
SOFTWARE.
|
|
40
|
+
--------------------------------------------------------------------------------
|