capsium 0.1.2 → 0.3.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.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +46 -7
  4. data/CHANGELOG.md +183 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE +21 -0
  7. data/README.adoc +496 -127
  8. data/Rakefile +8 -1
  9. data/bin/console +1 -1
  10. data/capsium.gemspec +12 -19
  11. data/exe/capsium +1 -1
  12. data/lib/capsium/cli/convert.rb +18 -0
  13. data/lib/capsium/cli/formatting.rb +27 -0
  14. data/lib/capsium/cli/package.rb +148 -0
  15. data/lib/capsium/cli/reactor.rb +33 -0
  16. data/lib/capsium/cli.rb +4 -96
  17. data/lib/capsium/converters/jekyll.rb +5 -7
  18. data/lib/capsium/converters.rb +7 -0
  19. data/lib/capsium/package/authentication.rb +38 -0
  20. data/lib/capsium/package/authentication_config.rb +78 -0
  21. data/lib/capsium/package/cipher.rb +197 -0
  22. data/lib/capsium/package/composition.rb +77 -0
  23. data/lib/capsium/package/dataset.rb +67 -34
  24. data/lib/capsium/package/dependency_resolver.rb +61 -0
  25. data/lib/capsium/package/encryption_config.rb +38 -0
  26. data/lib/capsium/package/manifest.rb +33 -22
  27. data/lib/capsium/package/manifest_config.rb +52 -7
  28. data/lib/capsium/package/merged_view.rb +168 -0
  29. data/lib/capsium/package/metadata.rb +4 -8
  30. data/lib/capsium/package/metadata_config.rb +97 -9
  31. data/lib/capsium/package/preparation.rb +81 -0
  32. data/lib/capsium/package/routes.rb +39 -41
  33. data/lib/capsium/package/routes_config.rb +174 -44
  34. data/lib/capsium/package/security.rb +140 -0
  35. data/lib/capsium/package/security_config.rb +60 -0
  36. data/lib/capsium/package/signer.rb +201 -0
  37. data/lib/capsium/package/storage.rb +20 -45
  38. data/lib/capsium/package/storage_config.rb +128 -3
  39. data/lib/capsium/package/store.rb +109 -0
  40. data/lib/capsium/package/testing/config_test.rb +70 -0
  41. data/lib/capsium/package/testing/context.rb +11 -0
  42. data/lib/capsium/package/testing/data_validation_test.rb +63 -0
  43. data/lib/capsium/package/testing/file_test.rb +45 -0
  44. data/lib/capsium/package/testing/report.rb +33 -0
  45. data/lib/capsium/package/testing/route_test.rb +64 -0
  46. data/lib/capsium/package/testing/test_case.rb +60 -0
  47. data/lib/capsium/package/testing/test_suite.rb +86 -0
  48. data/lib/capsium/package/testing.rb +20 -0
  49. data/lib/capsium/package/validator.rb +160 -0
  50. data/lib/capsium/package/verification.rb +32 -0
  51. data/lib/capsium/package/version.rb +204 -0
  52. data/lib/capsium/package.rb +92 -106
  53. data/lib/capsium/packager.rb +75 -6
  54. data/lib/capsium/reactor/authenticator.rb +180 -0
  55. data/lib/capsium/reactor/deploy.rb +71 -0
  56. data/lib/capsium/reactor/htpasswd.rb +154 -0
  57. data/lib/capsium/reactor/introspection.rb +91 -0
  58. data/lib/capsium/reactor/oauth2.rb +106 -0
  59. data/lib/capsium/reactor/serving.rb +75 -0
  60. data/lib/capsium/reactor/session.rb +82 -0
  61. data/lib/capsium/reactor.rb +93 -43
  62. data/lib/capsium/thor_ext.rb +1 -1
  63. data/lib/capsium/version.rb +1 -1
  64. data/lib/capsium.rb +7 -9
  65. data/sig/capsium/package/authentication.rbs +23 -0
  66. data/sig/capsium/package/authentication_config.rbs +71 -0
  67. data/sig/capsium/package/cipher.rbs +51 -0
  68. data/sig/capsium/package/composition.rbs +20 -0
  69. data/sig/capsium/package/dataset.rbs +28 -0
  70. data/sig/capsium/package/dependency_resolver.rbs +49 -0
  71. data/sig/capsium/package/encryption_config.rbs +35 -0
  72. data/sig/capsium/package/manifest.rbs +34 -0
  73. data/sig/capsium/package/manifest_config.rbs +38 -0
  74. data/sig/capsium/package/merged_view.rbs +46 -0
  75. data/sig/capsium/package/metadata.rbs +26 -0
  76. data/sig/capsium/package/metadata_config.rbs +49 -0
  77. data/sig/capsium/package/preparation.rbs +23 -0
  78. data/sig/capsium/package/routes.rbs +37 -0
  79. data/sig/capsium/package/routes_config.rbs +107 -0
  80. data/sig/capsium/package/security.rbs +72 -0
  81. data/sig/capsium/package/security_config.rbs +57 -0
  82. data/sig/capsium/package/signer.rbs +59 -0
  83. data/sig/capsium/package/storage.rbs +28 -0
  84. data/sig/capsium/package/storage_config.rbs +85 -0
  85. data/sig/capsium/package/store.rbs +37 -0
  86. data/sig/capsium/package/testing/config_test.rbs +21 -0
  87. data/sig/capsium/package/testing/context.rbs +14 -0
  88. data/sig/capsium/package/testing/data_validation_test.rbs +22 -0
  89. data/sig/capsium/package/testing/file_test.rbs +17 -0
  90. data/sig/capsium/package/testing/report.rbs +20 -0
  91. data/sig/capsium/package/testing/route_test.rbs +22 -0
  92. data/sig/capsium/package/testing/test_case.rbs +41 -0
  93. data/sig/capsium/package/testing/test_suite.rbs +20 -0
  94. data/sig/capsium/package/testing.rbs +7 -0
  95. data/sig/capsium/package/validator.rbs +29 -0
  96. data/sig/capsium/package/verification.rbs +24 -0
  97. data/sig/capsium/package/version.rbs +39 -0
  98. data/sig/capsium/package.rbs +61 -0
  99. data/sig/capsium/packager.rbs +32 -0
  100. data/sig/capsium/reactor/authenticator.rbs +40 -0
  101. data/sig/capsium/reactor/deploy.rbs +34 -0
  102. data/sig/capsium/reactor/htpasswd.rbs +36 -0
  103. data/sig/capsium/reactor/introspection.rbs +31 -0
  104. data/sig/capsium/reactor/oauth2.rbs +31 -0
  105. data/sig/capsium/reactor/serving.rbs +22 -0
  106. data/sig/capsium/reactor/session.rbs +36 -0
  107. data/sig/capsium/reactor.rbs +35 -0
  108. data/sig/capsium.rbs +3 -1
  109. metadata +108 -170
  110. data/lib/capsium/package/dataset_config.rb +0 -28
  111. data/lib/capsium/protector.rb +0 -103
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e2b954beb33b36d3300f8ff418f643a9afbbf688fad4e8333b195ccb76b38b41
4
- data.tar.gz: b2ef9e049b70ce3eb111ef0c136befaaf7a2f6a4d233ed9fb41a9d27d9c39e2c
3
+ metadata.gz: 188f04acf4c1f13b7a4515798e1183f59473ffba9200d375ea31b7ec2ceb705f
4
+ data.tar.gz: '068cb0fbbfa415b6f07bb299e233c2f27f5161fe82b932c1379883fcba79a36f'
5
5
  SHA512:
6
- metadata.gz: e3c75b5ab47ef3b7fe5a8f20571a300c15b960951c032d9c8d01e3eff295a237c294288613449ed090c73e38df718986f412573deeafd0f33acd7a43ead76c41
7
- data.tar.gz: 8bdb432b2b7efb4987d211608e8cb13828e2bf49d49cf921c3d9a7f7cc74f5a775ed6454e5bfd3e91a539a36fa0beb28395ff7910682d9d791f8c44db3eebe26
6
+ metadata.gz: ed6d82167bc38448608ced953d1c906980e2fbbbc1297e1c79debe3aaf67508a9aa14237741c42be113b4daffe4e8c8d3e7a8c710f899acebeece1ce9b4dfde8
7
+ data.tar.gz: c1294b0ec91effddc6cf7da04cb4c128db2a31f2f82625b2a12b92c303be6bcfc566f4bf4a8c6658a187f623e8ba433cab24d275a0d880b82c3717775aec3aa5
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /Gemfile.lock
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
data/.rubocop.yml CHANGED
@@ -1,12 +1,51 @@
1
- inherit_from:
2
- - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
3
-
4
- # local repo-specific modifications
5
- # ...
1
+ plugins:
2
+ - rubocop-performance
6
3
 
7
4
  AllCops:
8
- TargetRubyVersion: 2.6
5
+ TargetRubyVersion: 3.2
9
6
  NewCops: enable
7
+ SuggestExtensions: false
10
8
  Exclude:
11
- - 'lib/sts/mapper.rb'
12
9
  - 'vendor/**/*'
10
+
11
+ Layout/LineLength:
12
+ Max: 100
13
+
14
+ # Project convention: double-quoted string literals.
15
+ Style/StringLiterals:
16
+ EnforcedStyle: double_quotes
17
+
18
+ # Classes are intentionally undocumented for now; API docs land with a
19
+ # later phase together with the RBS signatures.
20
+ Style/Documentation:
21
+ Enabled: false
22
+
23
+ # RSpec example groups and the gemspec are block-heavy by nature.
24
+ Metrics/BlockLength:
25
+ Exclude:
26
+ - 'spec/**/*'
27
+ - 'capsium.gemspec'
28
+
29
+ # Spec helpers take the parameters the fixtures need.
30
+ Metrics/ModuleLength:
31
+ Exclude:
32
+ - 'spec/**/*'
33
+
34
+ # Spec helpers take the parameters the fixtures need; the reactor
35
+ # constructor takes one keyword per deploy-time concern.
36
+ Metrics/ParameterLists:
37
+ Max: 6
38
+ Exclude:
39
+ - 'spec/**/*'
40
+
41
+ Metrics/ClassLength:
42
+ Max: 120
43
+
44
+ Metrics/MethodLength:
45
+ Max: 25
46
+
47
+ Metrics/AbcSize:
48
+ Max: 30
49
+
50
+ Metrics/CyclomaticComplexity:
51
+ Max: 8
data/CHANGELOG.md ADDED
@@ -0,0 +1,183 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0
4
+
5
+ ### Added
6
+
7
+ - Digital signatures (RSA-SHA256, X.509) per the packaging and security
8
+ standard clauses: `Capsium::Package::Signer` signs a package directory in
9
+ place (`signature.sig` plus the embedded public key PEM, recorded as
10
+ `security.digitalSignatures {publicKey, signatureFile}` in
11
+ `security.json`) and verifies declared signatures. The signed payload is
12
+ the concatenation, in sorted package-relative path order, of the raw
13
+ bytes of every checksum-covered file (everything except `security.json`
14
+ and `signature.sig`) — verifiable independently with
15
+ `openssl dgst -sha256 -verify`.
16
+ - Signed packages are verified automatically on load:
17
+ `Capsium::Package#verify_signature`/`#verify_signature!`/`#signed?`;
18
+ a mismatch raises `Capsium::Package::Signer::SignatureMismatchError`,
19
+ so the reactor refuses to serve tampered signed packages.
20
+ - `capsium package sign PATH --key KEY.pem [--cert CERT.pem]` and
21
+ `capsium package verify-signature PATH [--cert CERT.pem]` (exit status 1
22
+ on unsigned packages and verification failures). Both accept a package
23
+ directory or a `.cap` file.
24
+ - Whole-package encryption (AES-256-GCM, RSA-OAEP-SHA256 wrapped DEK) per
25
+ the packaging standard's encryption clause: `Capsium::Package::Cipher`
26
+ encrypts a package directory or `.cap` into the standard encrypted
27
+ layout (`metadata.json` cleartext, `signature.json` encryption envelope,
28
+ `package.enc` AES-256-GCM of the inner `.cap` zip) and decrypts it back.
29
+ The OCB/OpenPGP alternatives mentioned by the standard are intentionally
30
+ not implemented.
31
+ - `capsium package encrypt PATH --public-key PUB.pem -o OUT.cap` and
32
+ `capsium package decrypt PATH --private-key PRIV.pem [-o OUT.cap]`.
33
+ - `Capsium::Package.new(path, decryption_key:)` transparently decrypts an
34
+ encrypted `.cap` (or uncompressed encrypted directory) on load; without
35
+ a key it raises `Capsium::Package::Cipher::KeyRequiredError` (the
36
+ reactor refuses to serve), and a wrong key or tampered ciphertext raises
37
+ `Capsium::Package::Cipher::DecryptionError` (GCM authentication).
38
+ - `Capsium::Packager#transform_cap`/`#with_unpacked_cap` helpers for
39
+ in-place `.cap` transformations.
40
+ - The Capsium package testing YAML DSL (05x-testing): packages declare
41
+ tests in `tests/*.yaml` files (top-level `tests` list), and
42
+ `capsium package test PATH` runs them with per-test `PASS`/`FAIL`
43
+ output, a summary line and proper exit codes. Supported test types:
44
+ `route` (expected status, optional `response_contains` and
45
+ `expected_content_type` against a reactor started for the run),
46
+ `file` (existence, optional `contains`), `data_validation` (dataset
47
+ rows validated against a JSON schema, JSON and YAML formats) and
48
+ `config` (configuration files parsed and the known package configs
49
+ validated against their canonical models). Implemented as an
50
+ open/closed registry of `Capsium::Package::Testing::TestCase`
51
+ subclasses run by `Capsium::Package::Testing::TestSuite`; invalid
52
+ test definitions are reported as failures instead of aborting the run.
53
+ - Layered storage (ARCHITECTURE.md section 5a): `storage.layers` stacks
54
+ overlay directories over `content/` (bottom -> top in declaration
55
+ order), each mirroring the `content/` tree.
56
+ `Capsium::Package::MergedView` — the merged-view home shared by the
57
+ package (validation) and the reactor (serving) — resolves content
58
+ top-first, honors `.capsium-tombstones` deletions (a tombstoned path
59
+ resolves 404 even when a lower layer holds the file, and a tombstone
60
+ also suppresses dependency content below it), and hides
61
+ `visibility: private` layers and non-exported resources in the
62
+ exported-only view dependents get. Packages without a `layers` config
63
+ behave exactly as before (single implicit `content/` layer).
64
+ - Composite packages (ARCHITECTURE.md section 4a):
65
+ `metadata.dependencies` (GUID -> semver range) resolve against a
66
+ package store (`CAPSIUM_STORE` or `--store`) of
67
+ `<name>-<version>.cap` files plus an optional `index.json` (GUID ->
68
+ file); the newest satisfying version wins via the built-in semver
69
+ matcher `Capsium::Package::Version`/`VersionRange` (`>=`, `^`, `~`,
70
+ exact, `*`, `x`-wildcards/partials, comma/space conjunctions — no new
71
+ runtime dependency). Resolution is recursive with circularity
72
+ detection and raises typed errors:
73
+ `Capsium::Package::DependencyNotFoundError`,
74
+ `UnsatisfiableDependencyError`, `CircularDependencyError`,
75
+ `DependencyVisibilityError`.
76
+ - Resolved dependencies become read-only layers below all of the
77
+ dependent's own layers, and only `exported` resources are visible;
78
+ routes may address dependency content explicitly as
79
+ `"<dependency-guid>/<path>"` and declare the route-inheritance
80
+ attributes of 05x-routing: `remap` (replaces the serving path),
81
+ `responseRewrite` (`body`, `headers`), `responseHeaders` (merged over
82
+ served headers) and `requestHeaders` (parsed and exposed for
83
+ forwarding reactors; a documented no-op for this static reactor).
84
+ Referencing a dependency's private or missing resource is a load-time
85
+ error. `capsium package info` prints the resolved dependency tree;
86
+ `capsium reactor serve` accepts `--store`.
87
+ - Authentication (05x-authentication, ARCHITECTURE.md section 4b):
88
+ `authentication.json` declares `basicAuth {enabled, passwdFile,
89
+ realm}` and `oauth2 {enabled, provider, clientId, authorizationUrl,
90
+ tokenUrl, userinfoUrl, redirectPath, scopes}`; the reactor enforces
91
+ it via `Capsium::Reactor::Authenticator`. Basic authentication
92
+ challenges (401 + `WWW-Authenticate`) and verifies against the
93
+ htpasswd file — bcrypt via the new `bcrypt` runtime dependency, plus
94
+ pure-Ruby md5-crypt (`$apr1$`/`$1$` as deployed by
95
+ htpasswd/OpenSSL/glibc), `{SHA}` and a `crypt(3)` fallback.
96
+ OAuth2 runs the authorization-code flow over net/http: `/auth/login`
97
+ redirects with HMAC-signed state, the callback exchanges the code at
98
+ `tokenUrl`, fetches the userinfo claims and establishes an
99
+ HMAC-SHA256 signed session cookie (`Capsium::Reactor::Session`).
100
+ Secrets never come from the package: `clientSecret`/`sessionSecret`
101
+ and role assignments live in `deploy.json` (`--deploy` or
102
+ `CAPSIUM_DEPLOY`); without a configured session secret the reactor
103
+ generates one and persists it (mode 0600) outside the package.
104
+ Dataset-route `accessControl {roles, authenticationRequired}` is
105
+ enforced after authentication: 401 unauthenticated, 403 unauthorized.
106
+ - The `content-validity` introspection entry now reports `signed` and
107
+ `encrypted` status, plus `signatureValid` when the package declares a
108
+ signature; `Capsium::Package#encrypted?` reports whether the package
109
+ was loaded from an encrypted source.
110
+
111
+ ### Changed
112
+
113
+ - Integrity checksums now exclude `signature.sig` in addition to
114
+ `security.json` (the signature signs the checksum-covered payload, so it
115
+ cannot be part of it).
116
+ - `capsium package pack` drops signing artifacts (`signature.sig`,
117
+ embedded public key) when repacking: signing is a post-pack step.
118
+ - The reactor serves resources through `Package#merged_view` (the
119
+ overlay-aware resolution path); behavior is unchanged for packages
120
+ without layers.
121
+ - Loading a package with declared `metadata.dependencies` now resolves
122
+ them eagerly against the package store (`store:` or `CAPSIUM_STORE`);
123
+ a package whose dependencies cannot resolve (including normalized
124
+ pre-0.2 dependency declarations) fails to load with a typed error.
125
+ - `Metrics/ParameterLists` rubocop budget raised to 6 for the reactor
126
+ constructor's per-concern keywords.
127
+
128
+ ## 0.2.0
129
+
130
+ ### Changed
131
+
132
+ - Migrated all package configuration models from shale to lutaml-model.
133
+ - All configuration files now follow the canonical schemas
134
+ (ARCHITECTURE.md sections 2-6):
135
+ - `metadata.json`: `dependencies` is an object of `guid -> semver range`;
136
+ `name`/`version`/`description`/`guid`/`uuid` are the required fields.
137
+ - `manifest.json`: a `resources` object keyed by package-relative path
138
+ (`type`, `visibility`, optional `version`).
139
+ - `routes.json`: optional top-level `index` plus a `routes` array with
140
+ discriminated kinds (`resource` / `dataset` / handler).
141
+ - `storage.json`: `storage.dataSets` object; dataset `source` and
142
+ `schemaFile` are package-relative paths.
143
+ - Legacy (pre-0.2) configuration files are accepted on read and normalized
144
+ to the canonical forms; writers emit only the canonical forms.
145
+ - Manifest and routes auto-generation is deterministic (sorted); HTML files
146
+ get dual routes, the index HTML gets `/`, datasets get
147
+ `/api/v1/data/<id>`.
148
+ - The reactor serves dataset routes as JSON, sets `Cache-Control: public,
149
+ max-age=31536000` on static resources (configurable), responds 501 to
150
+ handler routes, and rejects packages that fail integrity verification.
151
+ - The CLI exits nonzero on errors.
152
+
153
+ ### Added
154
+
155
+ - `security.json` with SHA-256 checksums over every package file, generated
156
+ at pack time; verified automatically on load (`Package#verify_integrity`,
157
+ `Capsium::Package::Security::IntegrityError` on mismatch).
158
+ - `capsium package validate <path|cap>`: per-check validation report
159
+ (metadata formats, on-disk existence of manifest resources, route targets
160
+ and dataset sources, dataset JSON-schema validation, checksums, external
161
+ http(s) references in content), exit status 1 on any failure.
162
+ - `capsium package unpack <cap> [-o dir]`.
163
+ - `Capsium::Package::Validator` and `Capsium::Package::Security`.
164
+ - Reactor introspection HTTP API (ARCHITECTURE.md section 7):
165
+ `GET /api/v1/introspect/metadata`, `/routes`, `/content-hashes` and
166
+ `/content-validity`, served as JSON by `Capsium::Reactor::Introspection`.
167
+ `content-hashes` is the SHA-256 of the `.cap` blob, or of the canonical
168
+ JSON serialization of the content checksums for directory sources;
169
+ `content-validity` re-verifies integrity live.
170
+ - Zip-slip protection on `.cap` extraction: entries escaping the
171
+ destination (absolute paths, drive letters, `..` segments) raise
172
+ `Capsium::Packager::UnsafeEntryError`.
173
+ - RBS signatures for the public API (`sig/`), kept green by the
174
+ `rbs:validate` rake task (part of the default rake task).
175
+ - `Capsium::Package#cap_file_path`.
176
+
177
+ ### Removed
178
+
179
+ - `Capsium::Protector` (bit-rotted, insecure; encryption/signing is
180
+ deferred to a later phase).
181
+ - shale dependency (replaced by lutaml-model); base64 dependency.
182
+ - Unused constants `Package::PACKAGING_FILE`, `Package::SIGNATURE_FILE`,
183
+ `Package::ENCRYPTED_PACKAGING_FILE`, `Routes::DEFAULT_INDEX_TARGET`.
data/Gemfile CHANGED
@@ -4,3 +4,9 @@ source "https://rubygems.org"
4
4
 
5
5
  # Specify your gem's dependencies in capsium.gemspec
6
6
  gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+ gem "rbs", "~> 4.0"
10
+ gem "rspec", "~> 3.11"
11
+ gem "rubocop", "~> 1.88"
12
+ gem "rubocop-performance", "~> 1.26"
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Ribose Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.