Merge pull request #1600 from harding/wallet-schema

Wallets: validate wallet YAML file using JSON Schema
This commit is contained in:
Will Binns 2017-05-20 08:55:21 -06:00 committed by GitHub
commit 4a25e43461
7 changed files with 375 additions and 5 deletions

View file

@ -23,6 +23,8 @@ group :development do
gem 'RedCloth' gem 'RedCloth'
gem 'therubyracer' # required by less gem 'therubyracer' # required by less
gem 'jshintrb', '~>0.3.0' gem 'jshintrb', '~>0.3.0'
gem 'safe_yaml'
gem 'json-schema'
end end
## Not used on build server. Only used by developers and Travis CI, so ## Not used on build server. Only used by developers and Travis CI, so

View file

@ -38,6 +38,8 @@ GEM
multi_json (>= 1.3) multi_json (>= 1.3)
rake rake
json (1.8.3) json (1.8.3)
json-schema (2.8.0)
addressable (>= 2.4)
kramdown (1.9.0) kramdown (1.9.0)
less (2.4.0) less (2.4.0)
commonjs (~> 0.2.7) commonjs (~> 0.2.7)
@ -77,6 +79,8 @@ DEPENDENCIES
jekyll (~> 3.0) jekyll (~> 3.0)
jshintrb (~> 0.3.0) jshintrb (~> 0.3.0)
json json
json-schema
kramdown kramdown
less (= 2.4.0) less (= 2.4.0)
safe_yaml
therubyracer therubyracer

View file

@ -59,8 +59,8 @@ pre-build-tests-fast: check-for-non-ascii-urls check-for-wrong-filename-assignme
check-bundle \ check-bundle \
check-for-english-in-en-dir \ check-for-english-in-en-dir \
check-for-consistent-bitcoin-core-titles \ 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 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 \ 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 ] \ ; if [ $$count -gt 14 ] \
; then echo "ERROR: too many wallets in $$platform platform. Remove one or change layout" \ ; then echo "ERROR: too many wallets in $$platform platform. Remove one or change layout" \
; fi ; done ; 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

31
_contrib/schema-validator.rb Executable file
View file

@ -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 <schema-file> <file-to-validate>"
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

View file

@ -7,6 +7,7 @@
layout: base layout: base
id: choose-your-wallet id: choose-your-wallet
max_wallet_text_length: 418 ## Unicode characters
wallets: wallets:
- bitcoincore: - bitcoincore:
@ -261,7 +262,6 @@ wallets:
- android - android
- ios - ios
- windowsphone - windowsphone
- linux
check: check:
control: "checkgoodcontrolfull" control: "checkgoodcontrolfull"
validation: "checkfailvalidationcentralized" validation: "checkfailvalidationcentralized"
@ -1100,6 +1100,7 @@ wallets:
privacycheck: privacycheck:
privacyaddressreuse: "checkpassprivacyaddressrotation" privacyaddressreuse: "checkpassprivacyaddressrotation"
privacydisclosure: "checkfailprivacydisclosurecentralized" privacydisclosure: "checkfailprivacydisclosurecentralized"
privacynetwork: "checkfailprivacynetworknosupporttor"
- btc.com: - btc.com:
title: "BTC.com Bitcoin Wallet" title: "BTC.com Bitcoin Wallet"
titleshort: "BTC.com" titleshort: "BTC.com"
@ -1231,7 +1232,9 @@ wallets:
<div class="check{{ check[0] }} {% if check[1] contains "checkgood" %}checkgood{% elsif check[1] contains "checkpass" %}checkpass{% elsif check[1] contains "checkneutral" %}checkneutral{% else %}checkfail{% endif %}">{% translate {{check[1]}} %}<div><p>{% translate {{check[1]}}txt %}</p><span></span></div></div> <div class="check{{ check[0] }} {% if check[1] contains "checkgood" %}checkgood{% elsif check[1] contains "checkpass" %}checkpass{% elsif check[1] contains "checkneutral" %}checkneutral{% else %}checkfail{% endif %}">{% translate {{check[1]}} %}<div><p>{% translate {{check[1]}}txt %}</p><span></span></div></div>
{% endif %}{% endfor %} {% endif %}{% endfor %}
</div> </div>
<p>{% translate {{platform.text}} %}</p> {% capture platform_text %}{% translate {{platform.text}} %}{% endcapture %}
{% assign platform_text_length = platform_text | size %}
<p>{% if platform_text_length > page.max_wallet_text_length %}{% die Wallet text too long %}{% else %}{{platform_text}}{% endif %}</p>
<div><img src="/img/screenshots/{{ platform.screenshot }}" alt="screenshot"></div> <div><img src="/img/screenshots/{{ platform.screenshot }}" alt="screenshot"></div>
</div> </div>
<a><img src="/img/wallet/{{ wallet[0] }}.png" alt="{{ wallet[1].title }}" />{{ wallet[1].titleshort }}<span></span></a> <a><img src="/img/wallet/{{ wallet[0] }}.png" alt="{{ wallet[1].title }}" />{{ wallet[1].titleshort }}<span></span></a>
@ -1261,7 +1264,9 @@ wallets:
<div class="check{{ check[0] }} {% if check[1] contains "checkgood" %}checkgood{% elsif check[1] contains "checkpass" %}checkpass{% elsif check[1] contains "checkneutral" %}checkneutral{% else %}checkfail{% endif %}">{% translate {{check[1]}} %}<div><p>{% translate {{check[1]}}txt %}</p><span></span></div></div> <div class="check{{ check[0] }} {% if check[1] contains "checkgood" %}checkgood{% elsif check[1] contains "checkpass" %}checkpass{% elsif check[1] contains "checkneutral" %}checkneutral{% else %}checkfail{% endif %}">{% translate {{check[1]}} %}<div><p>{% translate {{check[1]}}txt %}</p><span></span></div></div>
{% endif %}{% endfor %} {% endif %}{% endfor %}
</div> </div>
<p>{% translate {{platform[1].text}} %}</p> {% capture platform_text %}{% translate {{platform[1].text}} %}{% endcapture %}
{% assign platform_text_length = platform_text | size %}
<p>{% if platform_text_length > page.max_wallet_text_length %}{% die Wallet text too long %}{% else %}{{platform_text}}{% endif %}</p>
<div><img src="/img/screenshots/{{ platform[1].screenshot }}" alt="screenshot"></div> <div><img src="/img/screenshots/{{ platform[1].screenshot }}" alt="screenshot"></div>
</div> </div>
<a><img src="/img/wallet/{{ wallet[0] }}.png" alt="{{ wallet[1].title }}" />{{ wallet[1].titleshort }}<span></span></a> <a><img src="/img/wallet/{{ wallet[0] }}.png" alt="{{ wallet[1].title }}" />{{ wallet[1].titleshort }}<span></span></a>

View file

@ -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 To get a passing score, the wallet must avoid address reuse by using a new
change address for each transaction. 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.

View file

@ -0,0 +1,318 @@
---
# 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
max_wallet_text_length:
description: >
The global maximum length for a wallet text description in any
langugage (counted in Unicode characters). Ensures text doesn't
overfill its box.
type: integer
## 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 that 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