diff --git a/Gemfile b/Gemfile index 7d84ca16..17d14efd 100644 --- a/Gemfile +++ b/Gemfile @@ -23,6 +23,8 @@ group :development do gem 'RedCloth' gem 'therubyracer' # required by less gem 'jshintrb', '~>0.3.0' + gem 'safe_yaml' + gem 'json-schema' end ## Not used on build server. Only used by developers and Travis CI, so diff --git a/Gemfile.lock b/Gemfile.lock index 01d129ce..2036a853 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -38,6 +38,8 @@ GEM multi_json (>= 1.3) rake json (1.8.3) + json-schema (2.8.0) + addressable (>= 2.4) kramdown (1.9.0) less (2.4.0) commonjs (~> 0.2.7) @@ -77,6 +79,8 @@ DEPENDENCIES jekyll (~> 3.0) jshintrb (~> 0.3.0) json + json-schema kramdown less (= 2.4.0) + safe_yaml therubyracer diff --git a/Makefile b/Makefile index 8eabb82d..5dc1e982 100644 --- a/Makefile +++ b/Makefile @@ -59,8 +59,8 @@ pre-build-tests-fast: check-for-non-ascii-urls check-for-wrong-filename-assignme check-bundle \ check-for-english-in-en-dir \ check-for-consistent-bitcoin-core-titles \ - check-for-too-many-wallets-on-one-platform - + check-for-too-many-wallets-on-one-platform \ + check-validate-yaml \ ## Post-build tests which, aggregated together, take less than 10 seconds to run on a typical PC post-build-tests-fast: check-for-build-errors ensure-each-svg-has-a-png check-for-liquid-errors \ @@ -293,3 +293,7 @@ check-for-too-many-wallets-on-one-platform: ; if [ $$count -gt 14 ] \ ; then echo "ERROR: too many wallets in $$platform platform. Remove one or change layout" \ ; fi ; done + +check-validate-yaml: +## Validate YAML files against schemas + $S bundle exec _contrib/schema-validator.rb quality-assurance/schemas/wallets.yaml _templates/choose-your-wallet.html diff --git a/_contrib/schema-validator.rb b/_contrib/schema-validator.rb new file mode 100755 index 00000000..aa310a35 --- /dev/null +++ b/_contrib/schema-validator.rb @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby +# This file is licensed under the MIT License (MIT) available on +# http://opensource.org/licenses/MIT. + +require 'safe_yaml/load' +require 'json-schema' + +if ARGV[1].nil? + puts "Usage: schema-validator.rb " + exit(255) +end + +schema_file = ARGV[0] +file_to_validate = ARGV[1] + +file = File.open(schema_file, 'r') +schema = SafeYAML.load(file) +file.close() + +file = File.open(file_to_validate, 'r') +document = SafeYAML.load(file) +file.close() + +results = JSON::Validator.fully_validate(schema, document) + +if results.empty? + exit(0) +else + puts results + exit(1) +end diff --git a/_templates/choose-your-wallet.html b/_templates/choose-your-wallet.html index cf87581b..516d6888 100755 --- a/_templates/choose-your-wallet.html +++ b/_templates/choose-your-wallet.html @@ -261,7 +261,6 @@ wallets: - android - ios - windowsphone - - linux check: control: "checkgoodcontrolfull" validation: "checkfailvalidationcentralized" @@ -1100,6 +1099,7 @@ wallets: privacycheck: privacyaddressreuse: "checkpassprivacyaddressrotation" privacydisclosure: "checkfailprivacydisclosurecentralized" + privacynetwork: "checkfailprivacynetworknosupporttor" - btc.com: title: "BTC.com Bitcoin Wallet" titleshort: "BTC.com" diff --git a/docs/managing-wallets.md b/docs/managing-wallets.md index 3f26afdf..d13a0cd4 100644 --- a/docs/managing-wallets.md +++ b/docs/managing-wallets.md @@ -156,3 +156,9 @@ servers and be compatible with Tor. To get a passing score, the wallet must avoid address reuse by using a new change address for each transaction. + +### Schema validation + +Wallet entries are validated against the schema in +`quality-assurance/schemas/wallets.yaml` and you will find a +description of every available option in that file. diff --git a/quality-assurance/schemas/wallets.yaml b/quality-assurance/schemas/wallets.yaml new file mode 100644 index 00000000..51ca8bdf --- /dev/null +++ b/quality-assurance/schemas/wallets.yaml @@ -0,0 +1,311 @@ +--- +# This file is licensed under the MIT License (MIT) available on +# http://opensource.org/licenses/MIT. +id: https://bitcoin.org/quality-assurance/schemas/wallets.yaml +description: The main container +type: object +required: + - layout + - id + - wallets +additionalProperties: false +properties: + layout: + description: The Jekyll page layout to use + type: string + enum: + - base + + id: + description: The page identifier to use for translations + type: string + + ## The wallets array + wallets: + description: The array containing all wallets + type: array + uniqueItems: true + items: + description: The wallets. Each value is an arbitrary ID + type: object + additionalProperties: ## matches anything since this is an arbitrary ID string + required: + - title + - titleshort + - compat + - level + - platform + additionalProperties: false + properties: + title: + description: The full wallet name; displayed when you view the full listing + type: string + minLength: 1 + maxLength: 100 + + titleshort: + description: A short name displayed on the overview page + type: string + minLength: 1 + maxLength: 20 + + ## TODO: change this into an array so we can practically test values + compat: + description: A complete list of platforms supported by the wallet + type: string + minLength: 1 + + ## TODO: we should probably use names here and translate to + ## numbers (if required) in the template + level: + description: | + The wallet's overall security level. + 1 - full nodes + 2 - SPV using random servers (e.g. P2P SPV, Electrum servers, etc...) + 3 - Hybrid & multisig wallets + 4 - Web wallets + + type: integer + minimum: 1 + maximum: 4 + + ## The platforms the wallet supports + platform: + description: the platforms the wallet supports + type: object + additionalProperties: false + ## Wallets that want to use different names, screenshots, + ## download URLs, or scoring for different versions (e.g. an + ## Android version and an iOS version) must declare both a + ## default platform for the main platform (desktop, mobile, + ## hardware, web) and then a sub-platform in the platform field + ## for any entries that diverge from the default (android, ios, + ## blackberry, etc...). We use a long hand-maintained list of + ## dependencies here to ensure that any wallet using a + ## sub-platform also declares main platform. + # + ## TODO: refactor YAML to get rid of this weirdness + dependencies: + android: + - mobile + ios: + - mobile + blackberry: + - mobile + windowsphone: + - mobile + windows: + - desktop + linux: + - desktop + mac: + - desktop + properties: + desktop: + description: Wallets than run on desktop operating systems + type: object + required: &desktop-required + - text + - link + # - source ## TODO: Not currently required, but we should aim for making it required + - screenshot + - os + - check + - privacycheck + additionalProperties: false + properties: + text: &text + description: > + The identifier for the wallet's translation string. + Also used for the wallet's icon within the /img/wallet/ + directory. + type: string + link: &link + description: The download URL + type: string + format: uri + source: &source + description: The source code download URL + type: string + format: uri + screenshot: &screenshot + description: File name for screenshot within the /img/screenshots/ directory + type: string + os: + description: The specific operating systems the wallet supports + type: array + uniqueItems: true + items: + type: string + enum: + - windows + - mac + - linux + check: &check + description: The wallet's features (or lack thereof) + type: object + required: + - control + - validation + - transparency + - environment + - privacy + # - fees ## TODO: make required when web wallets moved + additionalProperties: false + properties: + control: + description: How much control the user has over their money (e.g. private keys) + type: string + enum: + - checkgoodcontrolfull + - checkpasscontrolhybrid + - checkpasscontrolmulti + - checkfailcontrolthirdpartyinsured + - checkfailcontrolthirdparty + validation: + description: How much assurance the user has that their bitcoins are real + type: string + enum: + - checkgoodvalidationfullnode + - checkgoodvalidationfullnoderequired + - checkneutralvalidationvariable + - checkpassvalidationspvp2p + - checkpassvalidationspvservers + - checkpassvalidationservers + - checkfailvalidationcentralized + transparency: + description: How much insight the user has into the code they run + type: string + enum: + - checkgoodtransparencydeterministic + - checkpasstransparencyopensource + - checkpasstransparencyopensourcehardware + - checkpasstransparencyopenspechardware + - checkfailtransparencyclosedsource + - checkfailtransparencyremote + - checkfailtransparencynew + environment: + description: How secure is the environment the wallet runs in + type: string + enum: + - checkgoodenvironmenthardware + - checkpassenvironmentmobile + - checkpassenvironmenttwofactor + - checkfailenvironmentdesktop + privacy: + description: How much privacy the wallet gives the user + type: string + enum: + - checkgoodprivacyimproved + - checkpassprivacybasic + - checkneutralprivacyvariable + - checkfailprivacyweak + fees: + description: How does the wallet decide what fee to pay + type: string + enum: + - checkgoodfeecontrolfull + - checkpassfeecontroldynamic + - checkpassfeecontroloverride + - checkneutralfeecontrolvariable + - checkfailfeecontrolstatic + privacycheck: &privacycheck + description: Details about the wallet's privacy settings + type: object + required: + - privacyaddressreuse + - privacydisclosure + - privacynetwork + additionalProperties: false + properties: + privacyaddressreuse: + description: Does the wallet default to address reuse? + type: string + enum: + - checkpassprivacyaddressrotation + - checkfailprivacyaddressrotation + privacydisclosure: + description: To whom, if anyone, does the wallet reveal the addresses it uses? + type: string + enum: + - checkpassprivacydisclosurefullnode + - checkfailprivacydisclosurespv + - checkfailprivacydisclosurecentralized + - checkfailprivacydisclosureaccount + privacynetwork: + description: What network-level privacy options does the wallet support + type: string + enum: + - checkpassprivacynetworksupporttorproxy + - checkfailprivacynetworknosupporttor + + + mobile: &mobile + description: Wallets that run on mobile devices with limited operating systems + type: object + required: *desktop-required + properties: + text: *text + link: *link + source: *source + screenshot: *screenshot + os: + description: The specific operating systems the wallet supports + type: array + uniqueItems: true + items: + type: string + enum: + - android + - blackberry + - ios + - windowsphone + check: *check + privacycheck: *privacycheck + ## Aliases for the mobile platform to allow setting + ## specific download URLs and screenshots for apps on + ## different mobile platforms + android: *mobile + ios: *mobile + blackberry: *mobile + windowsphone: *mobile + + web: + description: Wallets that run in a web browser + type: object + required: + - text + - link + # - source ## Not required :-( + - screenshot + - os + - check + - privacycheck + additionalProperties: false + properties: + text: *text + link: *link + source: *source + screenshot: *screenshot + os: + type: null + check: *check + privacycheck: *privacycheck + + hardware: + description: Transaction-signing hardware that works in conjunction with software that interacts with the network + type: object + required: + - text + - link + - source + - screenshot + - check + additionalProperties: false + properties: + text: *text + link: *link + source: *source + screenshot: *screenshot + check: *check +