shc_vaccination_test_kit 0.2.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dbdd993ca6764d4b7b4137f21c6b03b5abff56ebe630ec9306b76c824ecdc1da
4
- data.tar.gz: 893338ec3107db24033fff60c9f0a0f86d77768654ccdb7e49d983df6b6c605f
3
+ metadata.gz: fba6c400d4a9ac873d20b71a7ee8265e162d63e3862d63d6b596c9c457dc2c91
4
+ data.tar.gz: dbf2ac6446acf88efe2826f5293f921ca1e6b4bfafec745a284bb8fefbf55da2
5
5
  SHA512:
6
- metadata.gz: 5d2fbb5473a7e8f2601311785779ae06e440e91523431be44f0c528b6ee1640e617b16f61d138c5dcf2201c2207da6e29b2acfa263d2ec8000d0f2bbf97607a4
7
- data.tar.gz: 6f5ac3f76f69479159ca387f0fa0fb76676a061bc2c360b122e0011413cdc9342625b3da1e7f4f64ea31386fbe8837d8f5afb042539742ad4179d2870f500614
6
+ metadata.gz: 481be6a8e337ff9bc9c419c64c3b3bc0f087ab89e813b0578a67b6b78d9113e79c8e78894d468d3510bf46a612599297b73d392b1c0366406bacbaf09f977145
7
+ data.tar.gz: c27435d742c29237945d94c54f2acc0416e5d06e3d4b10d2aaa457a6f000cbf58fe8e52d53e13a932d31397059c47f050aeafa6538f693f98d50ed79c915d712
@@ -0,0 +1,21 @@
1
+ # Note on this IGs folder
2
+
3
+ There are three reasons why it would be necessary to put an IG package.tgz in this folder. If none of these apply, you do not need to put any files here, or can consider removing any existing files to make it clear they are unused.
4
+
5
+ ## 1. Generated Test Suites
6
+ Some test kits use a "generator" to automatically generate the contents of a test suite for an IG. The IG files are required every time the test suites need to be regenerated. Examples of test kits that use this approach are the US Core Test Kit and CARIN IG for Blue Button® Test Kit.
7
+
8
+
9
+ ## 2. Non-published IG
10
+ If your IG, or the specific version of the IG you want to test against, is not published, then the validator service needs to load the IG from file in order to be able to validate resources with it. The IG must be referenced in the `fhir_resource_validator` block in the test suite definition by filename, ie:
11
+
12
+ ```ruby
13
+ fhir_resource_validator do
14
+ igs 'igs/filename.tgz'
15
+
16
+ ...
17
+ end
18
+ ```
19
+
20
+ ## 3. Inferno Validator UI
21
+ The Inferno Validator UI is configured to auto-load any IGs present in the igs folder and include them in all validations. The Inferno Validator UI is currently disabled by default, so this is only relevant if you choose to re-enable it. In general, the Inferno team is currently leaving IGs in this folder even if not otherwise necessary to make it easy to re-enable the validator UI.
@@ -0,0 +1,54 @@
1
+ require_relative 'version'
2
+
3
+ module SHCVaccinationTestKit
4
+ class Metadata < Inferno::TestKit
5
+ id :shc_vaccination_test_kit
6
+ title 'SMART Health Cards: Vaccination and Testing Test Kit'
7
+ description <<~DESCRIPTION
8
+ The SMART Health Cards Vaccination: Vaccination and Testing Test Kit provides an
9
+ executable set of tests for the [SMART Health Cards: Vaccinations and Testing Implementation Guide v0.5.0-rc](https://www.hl7.org/fhir/uv/shc-vaccination/2021Sep/). This test kit
10
+ simulates downloading and validating a SMART Health Card.
11
+ <!-- break -->
12
+
13
+ This test kit is [open source](https://github.com/inferno-framework/shc-vaccination-test-kit#license) and freely available for use or
14
+ adoption by the health IT community including EHR vendors, health app
15
+ developers, and testing labs. It is built using the [Inferno Framework](https://inferno-framework.github.io/inferno-core/). The Inferno Framework is
16
+ designed for reuse and aims to make it easier to build test kits for any
17
+ FHIR-based data exchange.
18
+
19
+ ## Status
20
+
21
+ These tests are intended to allow server implementers to perform checks of their server against SMART Health Card: Vaccination & Testing requrirements.
22
+
23
+ The test kit currently tests the following requirements:
24
+ - Download and validate a health card via file download
25
+ - Download and validate a health card via FHIR $health-cards-issue operation
26
+
27
+ See the test descriptions within the test kit for detail on the specific validations performed as part of testing these requirements.
28
+
29
+ This test kit does not test:
30
+ - Decoding of QR codes
31
+
32
+ ## Repository
33
+
34
+ The SMART Health Cards Vaccination Test Kit GitHub repository can be [found here](https://github.com/inferno-framework/shc-vaccination-test-kit).
35
+
36
+ ## Providing Feedback and Reporting Issues
37
+
38
+ We welcome feedback on the tests, including but not limited to the following areas:
39
+
40
+ - Validation logic, such as potential bugs, lax checks, and unexpected failures.
41
+ - Requirements coverage, such as requirements that have been missed, tests that necessitate features that the IG does not require, or other issues with the interpretation of the IG's requirements.
42
+ - User experience, such as confusing or missing information in the test UI.
43
+
44
+ Please report any issues with this set of tests in the [issues section](https://github.com/inferno-framework/shc-vaccination-test-kit/issues) of the repository.
45
+ DESCRIPTION
46
+ suite_ids [:shc_vaccination]
47
+ tags ['SMART Health Cards']
48
+ last_updated LAST_UPDATED
49
+ version VERSION
50
+ maturity 'Low'
51
+ authors ['Stephen MacVicar']
52
+ repo 'https://github.com/inferno-framework/smart-health-cards-test-kit'
53
+ end
54
+ end
@@ -0,0 +1,60 @@
1
+ require 'inferno'
2
+
3
+ module SHCVaccinationTestKit
4
+ class SHCVaccinationFHIRValidation < Inferno::Test
5
+ include SmartHealthCardsTestKit::HealthCard
6
+
7
+ id :shc_vaccination_validation_test
8
+ title 'test Health Card payloads conform to the Vaccination Credential Bundle Profiles'
9
+ description %(
10
+ SMART Health Card (SHC) for vaccination records payload SHALL be a valid FHIR Bundle resource
11
+ )
12
+ input :fhir_bundles
13
+
14
+ run do
15
+ skip_if fhir_bundles.blank?, 'No FHIR bundles received'
16
+
17
+ assert_valid_json(fhir_bundles)
18
+ bundle_array = JSON.parse(fhir_bundles)
19
+
20
+ skip_if bundle_array.blank?, 'No FHIR bundles received'
21
+
22
+ bundle_array.each do |bundle|
23
+ validate_fhir_bundle(FHIR::Bundle.new(bundle))
24
+ end
25
+ end
26
+
27
+ def validate_fhir_bundle(bundle)
28
+ # assert bundle.entry.any? { |r| r.resource.is_a?(FHIR::Immunization) } || bundle.entry.any? { |r| r.resource.is_a?(FHIR::Observation) },
29
+ # "Bundle must have either Immunization entries or Observation entries"
30
+
31
+ # if bundle.entry.any? { |r| r.resource.is_a?(FHIR::Immunization) }
32
+ assert_valid_resource(
33
+ resource: bundle,
34
+ profile_url: 'http://hl7.org/fhir/uv/smarthealthcards-vaccination/StructureDefinition/vaccination-credential-bundle'
35
+ )
36
+
37
+ warning do
38
+ assert_valid_resource(
39
+ resource: bundle,
40
+ profile_url: 'http://hl7.org/fhir/uv/smarthealthcards-vaccination/StructureDefinition/vaccination-credential-bundle-dm'
41
+ )
42
+ end
43
+ # end
44
+
45
+ if bundle.entry.any? { |r| r.resource.is_a?(FHIR::Observation) }
46
+ assert_valid_resource(
47
+ resource: bundle,
48
+ profile_url: 'http://hl7.org/fhir/uv/smarthealthcards-vaccination/StructureDefinition/covid19-laboratory-bundle'
49
+ )
50
+
51
+ warning do
52
+ assert_valid_resource(
53
+ resource: bundle,
54
+ profile_url: 'http://hl7.org/fhir/uv/smarthealthcards-vaccination/StructureDefinition/covid19-laboratory-bundle-dm'
55
+ )
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,4 @@
1
+ module SHCVaccinationTestKit
2
+ VERSION = '0.4.0'.freeze
3
+ LAST_UPDATED = '2025-03-11'.freeze # TODO: update next release
4
+ end
@@ -1,29 +1,50 @@
1
- require 'health_cards'
2
- require_relative 'covid19_vci/file_download'
3
- require_relative 'covid19_vci/fhir_operation'
1
+ require 'smart_health_cards_test_kit'
2
+ require_relative 'shc_vaccination_test_kit/shc_vaccination_validation_test'
3
+ require_relative 'shc_vaccination_test_kit/metadata'
4
4
 
5
- module Covid19VCI
6
- class Suite < Inferno::TestSuite
7
- id 'c19-vci'
5
+ module SHCVaccinationTestKit
6
+ class SHCVaccinationSuite < Inferno::TestSuite
7
+ id 'shc_vaccination'
8
8
  title 'SMART Health Cards: Vaccination & Testing'
9
9
  description %(
10
10
  This test suite evaluates the ability of a system to provide
11
- access to [SMART Health Cards](https://smarthealth.cards/) via file download or HL7® FHIR® API.
11
+ access to [SMART Health Cards Vaccination and Testing](https://hl7.org/fhir/uv/shc-vaccination/2021Sep/index.html)
12
+ resources via file download, HL7® FHIR® API, or QR Scanning.
12
13
  )
14
+ source_code_url('https://github.com/inferno-framework/shc-vaccination-test-kit')
15
+ download_url('https://github.com/inferno-framework/shc-vaccination-test-kit/releases')
16
+ report_issue_url('https://github.com/inferno-framework/shc-vaccination-test-kit/issues')
13
17
 
14
18
  VALIDATION_MESSAGE_FILTERS = [
15
19
  /\A\S+: \S+: URL value '.*' does not resolve/,
16
20
  ].freeze
17
21
 
18
22
  fhir_resource_validator do
19
- igs 'igs/hl7.fhir.uv.smarthealthcards-vaccination-0.5.0-rc.tgz'
23
+ igs('igs/hl7.fhir.uv.smarthealthcards-vaccination-0.5.0-rc.tgz')
20
24
 
21
25
  exclude_message do |message|
22
26
  VALIDATION_MESSAGE_FILTERS.any? { |filter| filter.match? message.message }
23
27
  end
24
28
  end
25
29
 
26
- group from: :vci_file_download
27
- group from: :vci_fhir_operation
30
+ # Tests and TestGroups
31
+ # SmartHealthCardsTestKit::SmartHealthCardsTestSuite.groups.each do |group|
32
+ # test_group = group.ancestors[1]
33
+
34
+ # test_group.children.reject! { |test| test.id.include?('shc_fhir_validation_test') }
35
+ # test_group.test(from: :shc_vaccination_validation_test)
36
+
37
+ # group(from: test_group.id)
38
+ # end
39
+
40
+ def self.add_shc_group(group_id)
41
+ group from: group_id do
42
+ children.reject! { |test| test.id.include?('shc_fhir_validation_test') }
43
+ test from: :shc_vaccination_validation_test
44
+ end
45
+ end
46
+
47
+ add_shc_group :shc_file_download_group
48
+ add_shc_group :shc_fhir_operation_group
28
49
  end
29
50
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shc_vaccination_test_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen MacVicar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-05 00:00:00.000000000 Z
11
+ date: 2025-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: health_cards
14
+ name: inferno_core
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '='
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.2
19
+ version: 0.6.4
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '='
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.0.2
26
+ version: 0.6.4
27
27
  - !ruby/object:Gem::Dependency
28
- name: inferno_core
28
+ name: smart_health_cards_test_kit
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.4.37
33
+ version: 0.10.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.4.37
40
+ version: 0.10.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: database_cleaner-sequel
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -103,18 +103,17 @@ extensions: []
103
103
  extra_rdoc_files: []
104
104
  files:
105
105
  - LICENSE
106
- - lib/covid19_vci/fhir_operation.rb
107
- - lib/covid19_vci/file_download.rb
108
- - lib/covid19_vci/vc_fhir_validation.rb
109
- - lib/covid19_vci/vc_headers.rb
110
- - lib/covid19_vci/vc_payload_verification.rb
111
- - lib/covid19_vci/vc_signature_verification.rb
112
- - lib/covid19_vci/version.rb
113
106
  - lib/shc_vaccination_test_kit.rb
107
+ - lib/shc_vaccination_test_kit/igs/README.md
108
+ - lib/shc_vaccination_test_kit/igs/hl7.fhir.uv.smarthealthcards-vaccination-0.5.0-rc.tgz
109
+ - lib/shc_vaccination_test_kit/metadata.rb
110
+ - lib/shc_vaccination_test_kit/shc_vaccination_validation_test.rb
111
+ - lib/shc_vaccination_test_kit/version.rb
114
112
  homepage: https://github.com/inferno-framework/shc-vaccination-test-kit
115
113
  licenses:
116
114
  - Apache-2.0
117
115
  metadata:
116
+ inferno_test_kit: 'true'
118
117
  homepage_uri: https://github.com/inferno-framework/shc-vaccination-test-kit
119
118
  source_code_uri: https://github.com/inferno-framework/shc-vaccination-test-kit
120
119
  post_install_message:
@@ -125,14 +124,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
124
  requirements:
126
125
  - - ">="
127
126
  - !ruby/object:Gem::Version
128
- version: 3.1.2
127
+ version: 3.3.6
129
128
  required_rubygems_version: !ruby/object:Gem::Requirement
130
129
  requirements:
131
130
  - - ">="
132
131
  - !ruby/object:Gem::Version
133
132
  version: '0'
134
133
  requirements: []
135
- rubygems_version: 3.5.9
134
+ rubygems_version: 3.5.22
136
135
  signing_key:
137
136
  specification_version: 4
138
137
  summary: 'A collection of tests for the SMART Health Cards: Vaccination & Testing
@@ -1,105 +0,0 @@
1
- require_relative 'vc_fhir_validation'
2
- require_relative 'vc_headers'
3
- require_relative 'vc_payload_verification'
4
- require_relative 'vc_signature_verification'
5
-
6
- module Covid19VCI
7
- class FHIROperation < Inferno::TestGroup
8
- id 'vci_fhir_operation'
9
- title 'Download and validate a health card via FHIR $health-cards-issue operation'
10
-
11
- input :base_fhir_url, :patient_id
12
-
13
- fhir_client do
14
- url :base_fhir_url
15
- end
16
-
17
- test do
18
- title 'Server advertises health card support in its SMART configuration'
19
- id 'vci-fhir-01'
20
-
21
- run do
22
- get("#{base_fhir_url}/.well-known/smart-configuration")
23
-
24
- assert_response_status(200)
25
- assert_valid_json(response[:body])
26
-
27
- smart_configuration = JSON.parse(response[:body])
28
-
29
- assert smart_configuration['capabilities']&.include?('health-cards'),
30
- "SMART configuration does not list support for 'health-cards' capability"
31
- end
32
- end
33
-
34
- test do
35
- title 'Server advertises $health-cards-issue operation support in its CapabilityStatement'
36
- id 'vci-fhir-02'
37
-
38
- run do
39
- fhir_get_capability_statement
40
-
41
- assert_response_status(200)
42
-
43
- operations = resource.rest&.flat_map do |rest|
44
- rest.resource
45
- &.select { |r| r.type == 'Patient' && r.respond_to?(:operation) }
46
- &.flat_map(&:operation)
47
- end&.compact
48
-
49
- operation_defined = operations.any? { |operation| operation.name == 'health-cards-issue' }
50
-
51
- assert operation_defined,
52
- 'Server CapabilityStatement did not declare support for $health-cards-issue operation ' \
53
- 'on the Patient resource.'
54
- end
55
- end
56
-
57
- test do
58
- title 'Server returns a health card from the $health-cards-issue operation'
59
- id 'vci-fhir-03'
60
- output :credential_strings
61
-
62
- run do
63
- request_params = FHIR::Parameters.new(
64
- parameter: [
65
- {
66
- name: 'credentialType',
67
- valueUri: 'https://smarthealth.cards#covid19'
68
- }
69
- ]
70
- )
71
- fhir_operation("/Patient/#{patient_id}/$health-cards-issue", body: request_params)
72
-
73
- assert_response_status((200..207).to_a)
74
- assert_resource_type(:parameters)
75
-
76
- hc_parameters = resource.parameter.select { |parameter| parameter.name == 'verifiableCredential' }
77
-
78
- assert hc_parameters.present?, 'No COVID-19 health cards were returned'
79
- credential_strings = hc_parameters.map(&:value).join(',')
80
-
81
- output credential_strings: credential_strings
82
-
83
- count = hc_parameters.length
84
-
85
- pass "#{count} verifiable #{'credential'.pluralize(count)} received"
86
- end
87
- end
88
-
89
- test from: :vc_headers do
90
- id 'vci-fhir-04'
91
- end
92
-
93
- test from: :vc_signature_verification do
94
- id 'vci-fhir-05'
95
- end
96
-
97
- test from: :vc_payload_verification do
98
- id 'vci-fhir-06'
99
- end
100
-
101
- test from: :vc_fhir_verification do
102
- id 'vci-fhir-07'
103
- end
104
- end
105
- end
@@ -1,109 +0,0 @@
1
- require_relative 'vc_fhir_validation'
2
- require_relative 'vc_headers'
3
- require_relative 'vc_payload_verification'
4
- require_relative 'vc_signature_verification'
5
-
6
- module Covid19VCI
7
- class FileDownload < Inferno::TestGroup
8
- id 'vci_file_download'
9
- title 'Download and validate a health card via file download'
10
-
11
- input :file_download_url
12
-
13
- test do
14
- id 'vci-file-01'
15
- title 'Health card can be downloaded'
16
- description 'The health card can be downloaded and is a valid JSON object'
17
- makes_request :vci_file_download
18
-
19
- run do
20
- get(file_download_url, name: :vci_file_download)
21
-
22
- assert_response_status(200)
23
- assert_valid_json(response[:body])
24
- end
25
- end
26
-
27
- test do
28
- id 'vci-file-02'
29
- title 'Response contains correct Content-Type of application/smart-health-card'
30
- uses_request :vci_file_download
31
-
32
- run do
33
- skip_if request.status != 200, 'Health card not successfully downloaded'
34
-
35
- content_type = request.response_header('Content-Type')
36
-
37
- assert content_type.present?, 'Response did not include a Content-Type header'
38
- assert content_type.value.match?(%r{\Aapplication/smart-health-card(\z|\W)}),
39
- "Content-Type header was '#{content_type.value}' instead of 'application/smart-health-card'"
40
- end
41
- end
42
-
43
- test do
44
- id 'vci-file-03'
45
- title 'Health card is provided as a file download with a .smart-health-card extension'
46
- uses_request :vci_file_download
47
-
48
- run do
49
- skip_if request.status != 200, 'Health card not successfully downloaded'
50
-
51
- pass_if request.url.ends_with?('.smart-health-card')
52
-
53
- content_disposition = request.response_header('Content-Disposition')
54
- assert content_disposition.present?,
55
- "Url did not end with '.smart-health-card' and response did not include a Content-Disposition header"
56
-
57
- attachment_pattern = /\Aattachment;/
58
- assert content_disposition.value.match?(attachment_pattern),
59
- "Url did not end with '.smart-health-card' and " \
60
- "Content-Disposition header does not indicate file should be downloaded: '#{content_disposition}'"
61
-
62
- extension_pattern = /filename=".*\.smart-health-card"/
63
- assert content_disposition.value.match?(extension_pattern),
64
- "Url did not end with '.smart-health-card' and Content-Disposition header does not indicate " \
65
- "file should have a '.smart-health-card' extension: '#{content_disposition}'"
66
- end
67
- end
68
-
69
- test do
70
- id 'vci-file-04'
71
- title 'Response contains an array of Verifiable Credential strings'
72
- uses_request :vci_file_download
73
- output :credential_strings
74
-
75
- run do
76
- skip_if request.status != 200, 'Health card not successfully downloaded'
77
-
78
- body = JSON.parse(response[:body])
79
- assert body.include?('verifiableCredential'),
80
- "Health card does not contain 'verifiableCredential' field"
81
-
82
- vc = body['verifiableCredential']
83
-
84
- assert vc.is_a?(Array), "'verifiableCredential' field must contain an Array"
85
- assert vc.length.positive?, "'verifiableCredential' field must contain at least one verifiable credential"
86
-
87
- output credential_strings: vc.join(',')
88
-
89
- pass "Received #{vc.length} verifiable #{'credential'.pluralize(vc.length)}"
90
- end
91
- end
92
-
93
- test from: :vc_headers do
94
- id 'vci-file-05'
95
- end
96
-
97
- test from: :vc_signature_verification do
98
- id 'vci-file-06'
99
- end
100
-
101
- test from: :vc_payload_verification do
102
- id 'vci-file-07'
103
- end
104
-
105
- test from: :vc_fhir_verification do
106
- id 'vci-file-08'
107
- end
108
- end
109
- end
@@ -1,75 +0,0 @@
1
- module Covid19VCI
2
- class VCFHIRVerification < Inferno::Test
3
- title 'Health Card payloads conform to the Vaccination Credential Bundle Profiles'
4
- input :credential_strings
5
-
6
- id :vc_fhir_verification
7
-
8
- run do
9
- skip_if credential_strings.blank?, 'No Verifiable Credentials received'
10
-
11
- credential_strings.split(',').each do |credential|
12
- raw_payload = HealthCards::JWS.from_jws(credential).payload
13
- assert raw_payload&.length&.positive?, 'No payload found'
14
-
15
- decompressed_payload =
16
- begin
17
- Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(raw_payload)
18
- rescue Zlib::DataError
19
- assert false, 'Payload compression error. Unable to inflate payload.'
20
- end
21
-
22
- assert decompressed_payload.length.positive?, 'Payload compression error. Unable to inflate payload.'
23
-
24
- payload_length = decompressed_payload.length
25
- health_card = HealthCards::COVIDHealthCard.from_jws(credential)
26
- health_card_length = health_card.to_json.length
27
-
28
- assert_valid_json decompressed_payload, 'Payload is not valid JSON'
29
-
30
- payload = JSON.parse(decompressed_payload)
31
- vc = payload['vc']
32
- assert vc.is_a?(Hash), "Expected 'vc' claim to be a JSON object, but found #{vc.class}"
33
-
34
- subject = vc['credentialSubject']
35
- assert subject.is_a?(Hash), "Expected 'vc.credentialSubject' to be a JSON object, but found #{subject.class}"
36
-
37
- raw_bundle = subject['fhirBundle']
38
- assert raw_bundle.is_a?(Hash), "Expected 'vc.fhirBundle' to be a JSON object, but found #{raw_bundle.class}"
39
-
40
- bundle = FHIR::Bundle.new(raw_bundle)
41
-
42
- # assert bundle.entry.any? { |r| r.resource.is_a?(FHIR::Immunization) } || bundle.entry.any? { |r| r.resource.is_a?(FHIR::Observation) },
43
- # "Bundle must have either Immunization entries or Observation entries"
44
-
45
- # if bundle.entry.any? { |r| r.resource.is_a?(FHIR::Immunization) }
46
- assert_valid_resource(
47
- resource: bundle,
48
- profile_url: 'http://hl7.org/fhir/uv/smarthealthcards-vaccination/StructureDefinition/vaccination-credential-bundle'
49
- )
50
-
51
- warning do
52
- assert_valid_resource(
53
- resource: bundle,
54
- profile_url: 'http://hl7.org/fhir/uv/smarthealthcards-vaccination/StructureDefinition/vaccination-credential-bundle-dm'
55
- )
56
- end
57
- # end
58
-
59
- if bundle.entry.any? { |r| r.resource.is_a?(FHIR::Observation) }
60
- assert_valid_resource(
61
- resource: bundle,
62
- profile_url: 'http://hl7.org/fhir/uv/smarthealthcards-vaccination/StructureDefinition/covid19-laboratory-bundle'
63
- )
64
-
65
- warning do
66
- assert_valid_resource(
67
- resource: bundle,
68
- profile_url: 'http://hl7.org/fhir/uv/smarthealthcards-vaccination/StructureDefinition/covid19-laboratory-bundle-dm'
69
- )
70
- end
71
- end
72
- end
73
- end
74
- end
75
- end
@@ -1,21 +0,0 @@
1
- module Covid19VCI
2
- class VCHeaders < Inferno::Test
3
- title 'Health Cards contain the correct headers'
4
- input :credential_strings
5
-
6
- id :vc_headers
7
-
8
- run do
9
- skip_if credential_strings.blank?, 'No Verifiable Credentials received'
10
- credential_strings.split(',').each do |credential|
11
- header = HealthCards::JWS.from_jws(credential).header
12
-
13
- assert header['zip'] == 'DEF', "Expected 'zip' header to equal 'DEF', but found '#{header['zip']}'"
14
- assert header['alg'] == 'ES256', "Expected 'alg' header to equal 'ES256', but found '#{header['alg']}'"
15
- assert header['kid'].present?, "No 'kid' header was present"
16
- rescue StandardError => e
17
- assert false, "Error decoding credential: #{e.message}"
18
- end
19
- end
20
- end
21
- end
@@ -1,115 +0,0 @@
1
- module Covid19VCI
2
- class VCPayloadVerification < Inferno::Test
3
- title 'Health Card payloads follow the spec requirements'
4
- input :credential_strings
5
-
6
- id :vc_payload_verification
7
-
8
- run do
9
- skip_if credential_strings.blank?, 'No Verifiable Credentials received'
10
-
11
- credential_strings.split(',').each do |credential|
12
- raw_payload = HealthCards::JWS.from_jws(credential).payload
13
- assert raw_payload&.length&.positive?, 'No payload found'
14
-
15
- decompressed_payload =
16
- begin
17
- Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(raw_payload)
18
- rescue Zlib::DataError
19
- assert false, 'Payload compression error. Unable to inflate payload.'
20
- end
21
-
22
- assert decompressed_payload.length.positive?, 'Payload compression error. Unable to inflate payload.'
23
-
24
- payload_length = decompressed_payload.length
25
- health_card = HealthCards::HealthCard.from_jws(credential)
26
- health_card_length = health_card.to_json.length
27
-
28
- warning do
29
- assert payload_length <= health_card_length,
30
- "Payload may not be properly minified. Received a payload with length #{payload_length}, " \
31
- "but was able to generate a payload with length #{health_card_length}"
32
- end
33
-
34
- assert_valid_json decompressed_payload, 'Payload is not valid JSON'
35
-
36
- payload = JSON.parse(decompressed_payload)
37
-
38
- warning do
39
- nbf = payload['nbf']
40
- assert nbf.present?, "Payload does not include an 'nbf' claim"
41
- assert nbf.is_a?(Numeric), "Expected 'nbf' claim to be Numeric, but found #{nbf.class}"
42
- issue_time = Time.at(nbf).to_datetime
43
- assert issue_time < DateTime.now, "'nbf' is in the future: #{issue_time.rfc822}"
44
- end
45
-
46
- vc = payload['vc']
47
- assert vc.is_a?(Hash), "Expected 'vc' claim to be a JSON object, but found #{vc.class}"
48
- type = vc['type']
49
-
50
- warning do
51
- assert type.is_a?(Array), "Expected 'vc.type' to be an array, but found #{type.class}"
52
- assert type.include?('https://smarthealth.cards#health-card'),
53
- "'vc.type' does not include 'https://smarthealth.cards#health-card'"
54
- end
55
-
56
- subject = vc['credentialSubject']
57
- assert subject.is_a?(Hash), "Expected 'vc.credentialSubject' to be a JSON object, but found #{subject.class}"
58
-
59
- warning do
60
- assert subject['fhirVersion'].present?, "'vc.credentialSubject.fhirVersion' not provided"
61
- end
62
-
63
- raw_bundle = subject['fhirBundle']
64
- assert raw_bundle.is_a?(Hash), "Expected 'vc.fhirBundle' to be a JSON object, but found #{raw_bundle.class}"
65
-
66
- resource_scheme_regex = /\Aresource:\d+\z/
67
- warning do
68
- urls = raw_bundle['entry'].map { |entry| entry['fullUrl'] }
69
- bad_urls = urls.reject { |url| url.match?(resource_scheme_regex) }
70
- assert bad_urls.empty?,
71
- "The following Bundle entry urls do not use short resource-scheme URIs: #{bad_urls.join(', ')}"
72
- end
73
-
74
- bundle = FHIR::Bundle.new(raw_bundle)
75
- resources = bundle.entry.map(&:resource)
76
- bundle.entry.each { |entry| entry.resource = nil }
77
- resources << bundle
78
-
79
- resources.each do |resource|
80
- warning { assert resource.id.nil?, "#{resource.resourceType} resource should not have an 'id' element" }
81
-
82
- if resource.respond_to? :text
83
- warning { assert resource.text.nil?, "#{resource.resourceType} resource should not have a 'text' element" }
84
- end
85
-
86
- resource.each_element(resource) do |value, meta, path|
87
- case meta['type']
88
- when 'CodeableConcept'
89
- warning { assert value.text.nil?, "#{resource.resourceType} should not have a #{path}.text element" }
90
- when 'Coding'
91
- warning do
92
- assert value.display.nil?, "#{resource.resourceType} should not have a #{path}.display element"
93
- end
94
- when 'Reference'
95
- warning do
96
- next if value.reference.nil?
97
-
98
- assert value.reference.match?(resource_scheme_regex),
99
- "#{resource.resourceType}.#{path}.reference is not using the short resource URI scheme: " \
100
- "#{value.reference}"
101
- end
102
- when 'Meta'
103
- hash = value.to_hash
104
- warning do
105
- assert hash.length == 1 && hash.include?('security'),
106
- "If present, Bundle 'meta' field should only include 'security', " \
107
- "but found: #{hash.keys.join(', ')}"
108
- end
109
- end
110
- end
111
- end
112
- end
113
- end
114
- end
115
- end
@@ -1,69 +0,0 @@
1
- module Covid19VCI
2
- class VCSignatureVerification < Inferno::Test
3
- title 'Verifiable Credential signatures can be verified'
4
- input :credential_strings
5
-
6
- id :vc_signature_verification
7
-
8
- run do
9
- skip_if credential_strings.blank?, 'No Verifiable Credentials received'
10
- credential_strings.split(',').each do |credential|
11
- card = HealthCards::HealthCard.from_jws(credential)
12
- iss = card.issuer
13
-
14
- jws = HealthCards::JWS.from_jws(credential)
15
-
16
- assert iss.present?, 'Credential contains no `iss`'
17
- warning { assert iss.start_with?('https://'), "`iss` SHALL use the `https` scheme: #{iss}" }
18
- assert !iss.end_with?('/'), "`iss` SHALL NOT include a trailing `/`: #{iss}"
19
-
20
- key_set_url = "#{card.issuer}/.well-known/jwks.json"
21
-
22
- get(key_set_url)
23
-
24
- assert_response_status(200)
25
- assert_valid_json(response[:body])
26
-
27
- cors_header = request.response_header('Control-Allow-Origin')
28
- warning do
29
- assert cors_header.present?,
30
- 'No CORS header received. Issuers SHALL publish their public keys with CORS enabled'
31
- assert cors_header.value == '*',
32
- "Expected CORS header value of `*`, but actual value was `#{cors_header.value}`"
33
- end
34
-
35
- key_set = JSON.parse(response[:body])
36
-
37
- public_key = key_set['keys'].find { |key| key['kid'] == jws.kid }
38
- key_object = HealthCards::Key.from_jwk(public_key)
39
-
40
- assert public_key.present?, "Key set did not contain a key with a `kid` of #{jws.kid}"
41
-
42
- warning { assert public_key['kty'] == 'EC', "Key had a `kty` value of `#{public_key['kty']}` instead of `EC`" }
43
- warning do
44
- assert public_key['use'] == 'sig', "Key had a `use` value of `#{public_key['use']}` instead of `sig`"
45
- end
46
- warning do
47
- assert public_key['alg'] == 'ES256', "Key had an `alg` value of `#{public_key['alg']}` instead of `ES256`"
48
- end
49
- warning do
50
- assert public_key['crv'] == 'P-256', "Key had a `crv` value of `#{public_key['crv']}` instead of `P-256`"
51
- end
52
- warning { assert !public_key.include?('d'), 'Key SHALL NOT have the private key parameter `d`' }
53
- warning do
54
- assert public_key['kid'] == key_object.kid,
55
- "'kid' SHALL be equal to the base64url-encoded SHA-256 JWK Thumbprint of the key. " \
56
- "Received: '#{public_key['kid']}', Expected: '#{key_object.kid}'"
57
- end
58
-
59
- verifier = HealthCards::Verifier.new(keys: key_object, resolve_keys: false)
60
-
61
- begin
62
- assert verifier.verify(jws), 'JWS signature invalid'
63
- rescue StandardError => e
64
- assert false, "Error decoding credential: #{e.message}"
65
- end
66
- end
67
- end
68
- end
69
- end
@@ -1,3 +0,0 @@
1
- module Covid19VCI
2
- VERSION = '0.2.0'.freeze
3
- end