From ad2610c413da088f09f3930a00801ea99040ed24 Mon Sep 17 00:00:00 2001 From: Meisam <39205857+MFTabriz@users.noreply.github.com> Date: Thu, 15 Dec 2022 15:43:05 +0100 Subject: [PATCH] Validate nodeinfo response by schema (#21395) * add json-schema to :test in Gemfile * Create node_info_2.0_schema.json * test match_response_schema * Create match_response_schema.rb * Update nodeinfo_controller_spec.rb * Rename spec/support/node_info_2.0_schema.json to spec/support/schema/node_info_2.0_schema.json * Update match_response_schema.rb * cleanup * additionally validate the json schema itself disable throwing errors test the schema matcher * rename nodeinfo schema to nodeinfo_2.0 * use Rails.root.join to construct the path * prettify json * sync Gemfile.lock --- Gemfile | 5 +- Gemfile.lock | 3 + .../well_known/nodeinfo_controller_spec.rb | 2 + .../matchers/json/match_json_schema.rb | 6 + spec/support/schema/nodeinfo_2.0.json | 170 ++++++++++++++++++ 5 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 spec/support/matchers/json/match_json_schema.rb create mode 100644 spec/support/schema/nodeinfo_2.0.json diff --git a/Gemfile b/Gemfile index a399f51023..9fe2a11209 100644 --- a/Gemfile +++ b/Gemfile @@ -117,13 +117,14 @@ group :test do gem 'capybara', '~> 3.38' gem 'climate_control', '~> 0.2' gem 'faker', '~> 3.0' + gem 'json-schema', '~> 3.0' gem 'microformats', '~> 4.4' + gem 'rack-test', '~> 2.0' gem 'rails-controller-testing', '~> 1.0' + gem 'rspec_junit_formatter', '~> 0.6' gem 'rspec-sidekiq', '~> 3.1' gem 'simplecov', '~> 0.21', require: false gem 'webmock', '~> 3.18' - gem 'rspec_junit_formatter', '~> 0.6' - gem 'rack-test', '~> 2.0' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 578a88436b..ac3f560ea1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -344,6 +344,8 @@ GEM json-ld-preloaded (3.2.2) json-ld (~> 3.2) rdf (~> 3.2) + json-schema (3.0.0) + addressable (>= 2.8) jsonapi-renderer (0.2.2) jwt (2.4.1) kaminari (1.2.2) @@ -791,6 +793,7 @@ DEPENDENCIES idn-ruby json-ld json-ld-preloaded (~> 3.2) + json-schema (~> 3.0) kaminari (~> 1.2) kt-paperclip (~> 7.1) letter_opener (~> 1.8) diff --git a/spec/controllers/well_known/nodeinfo_controller_spec.rb b/spec/controllers/well_known/nodeinfo_controller_spec.rb index 694bb0fb9f..36e85f20d1 100644 --- a/spec/controllers/well_known/nodeinfo_controller_spec.rb +++ b/spec/controllers/well_known/nodeinfo_controller_spec.rb @@ -27,6 +27,8 @@ describe WellKnown::NodeInfoController, type: :controller do json = body_as_json + expect({ "foo" => 0 }).not_to match_json_schema("nodeinfo_2.0") + expect(json).to match_json_schema("nodeinfo_2.0") expect(json[:version]).to eq '2.0' expect(json[:usage]).to be_a Hash expect(json[:software]).to be_a Hash diff --git a/spec/support/matchers/json/match_json_schema.rb b/spec/support/matchers/json/match_json_schema.rb new file mode 100644 index 0000000000..5d9c9a618e --- /dev/null +++ b/spec/support/matchers/json/match_json_schema.rb @@ -0,0 +1,6 @@ +RSpec::Matchers.define :match_json_schema do |schema| + match do |input_json| + schema_path = Rails.root.join('spec', 'support', 'schema', "#{schema}.json").to_s + JSON::Validator.validate(schema_path, input_json, validate_schema: true) + end +end diff --git a/spec/support/schema/nodeinfo_2.0.json b/spec/support/schema/nodeinfo_2.0.json new file mode 100644 index 0000000000..085ce542bd --- /dev/null +++ b/spec/support/schema/nodeinfo_2.0.json @@ -0,0 +1,170 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://nodeinfo.diaspora.software/ns/schema/2.0#", + "description": "NodeInfo schema version 2.0.", + "type": "object", + "additionalProperties": false, + "required": [ + "version", + "software", + "protocols", + "services", + "openRegistrations", + "usage", + "metadata" + ], + "properties": { + "version": { + "description": "The schema version, must be 2.0.", + "enum": ["2.0"] + }, + "software": { + "description": "Metadata about server software in use.", + "type": "object", + "additionalProperties": false, + "required": ["name", "version"], + "properties": { + "name": { + "description": "The canonical name of this server software.", + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "version": { + "description": "The version of this server software.", + "type": "string" + } + } + }, + "protocols": { + "description": "The protocols supported on this server.", + "type": "array", + "minItems": 1, + "items": { + "enum": [ + "activitypub", + "buddycloud", + "dfrn", + "diaspora", + "libertree", + "ostatus", + "pumpio", + "tent", + "xmpp", + "zot" + ] + } + }, + "services": { + "description": "The third party sites this server can connect to via their application API.", + "type": "object", + "additionalProperties": false, + "required": ["inbound", "outbound"], + "properties": { + "inbound": { + "description": "The third party sites this server can retrieve messages from for combined display with regular traffic.", + "type": "array", + "minItems": 0, + "items": { + "enum": [ + "atom1.0", + "gnusocial", + "imap", + "pnut", + "pop3", + "pumpio", + "rss2.0", + "twitter" + ] + } + }, + "outbound": { + "description": "The third party sites this server can publish messages to on the behalf of a user.", + "type": "array", + "minItems": 0, + "items": { + "enum": [ + "atom1.0", + "blogger", + "buddycloud", + "diaspora", + "dreamwidth", + "drupal", + "facebook", + "friendica", + "gnusocial", + "google", + "insanejournal", + "libertree", + "linkedin", + "livejournal", + "mediagoblin", + "myspace", + "pinterest", + "pnut", + "posterous", + "pumpio", + "redmatrix", + "rss2.0", + "smtp", + "tent", + "tumblr", + "twitter", + "wordpress", + "xmpp" + ] + } + } + } + }, + "openRegistrations": { + "description": "Whether this server allows open self-registration.", + "type": "boolean" + }, + "usage": { + "description": "Usage statistics for this server.", + "type": "object", + "additionalProperties": false, + "required": ["users"], + "properties": { + "users": { + "description": "statistics about the users of this server.", + "type": "object", + "additionalProperties": false, + "properties": { + "total": { + "description": "The total amount of on this server registered users.", + "type": "integer", + "minimum": 0 + }, + "activeHalfyear": { + "description": "The amount of users that signed in at least once in the last 180 days.", + "type": "integer", + "minimum": 0 + }, + "activeMonth": { + "description": "The amount of users that signed in at least once in the last 30 days.", + "type": "integer", + "minimum": 0 + } + } + }, + "localPosts": { + "description": "The amount of posts that were made by users that are registered on this server.", + "type": "integer", + "minimum": 0 + }, + "localComments": { + "description": "The amount of comments that were made by users that are registered on this server.", + "type": "integer", + "minimum": 0 + } + } + }, + "metadata": { + "description": "Free form key value pairs for software specific values. Clients should not rely on any specific key present.", + "type": "object", + "minProperties": 0, + "additionalProperties": true + } + } +}