├── .eslintrc.js ├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── bin └── getVendors.sh ├── css ├── mds-explorer.css └── tabulator.min.css ├── favicon.png ├── img └── github-mark.svg ├── index.html ├── js ├── last-update.js ├── mds-explorer.js ├── mds.js ├── tabulator.min.js ├── vendors.js └── x509.js ├── mds.blob └── package.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "ecmaVersion": "latest", 9 | "sourceType": "module" 10 | }, 11 | "rules": { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # Workflow to update MDS blob and JSON in repo 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow every day 8 | schedule: 9 | - cron: '0 0,6,12,18 * * *' 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # jobs that can run sequentially or in parallel 15 | jobs: 16 | # This workflow contains a single job called "build" 17 | build: 18 | # The type of runner that the job will run on 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | # Checks-out repository under $GITHUB_WORKSPACE 23 | - uses: actions/checkout@v4 24 | 25 | - name: get remote vendors list 26 | env: 27 | CASPIO_ID: ${{ secrets.CASPIO_ID }} 28 | run: | 29 | cd bin 30 | chmod +x ./getVendors.sh 31 | ./getVendors.sh "$CASPIO_ID" 32 | cd - 33 | if [ "$(git status -s js/vendors.js | xargs)" != "" ] 34 | then 35 | echo "UAF vendors list updated" 36 | git config --local user.email "github-actions[bot]@users.noreply.github.com" 37 | git config --local user.name "github-actions[bot]" 38 | git add js/vendors.js 39 | git commit -m "UAF vendors list updated" 40 | git push 41 | else 42 | echo "No change in UAF vendors list" 43 | fi 44 | 45 | - name: get remote mds blob 46 | run: | 47 | wget -O mds.new https://mds.fidoalliance.org/ 48 | mv mds.new mds.blob 49 | 50 | - name: commit blob if new 51 | run: | 52 | if [ "$(git status -s mds.blob | xargs)" != "" ] 53 | then 54 | echo "MDS updated:" 55 | cut -d. -f2 mds.blob | tr '_-' '/+' | tr -d '\n' > mdsBody.b64 56 | let b64Size=$(wc -c mdsBody.b64 | awk '{ print $1 }') 57 | echo base64 size: ${b64Size} 58 | if [ "$((${b64Size} % 4))" == "2" ] 59 | then 60 | echo -n "==" >> mdsBody.b64 61 | fi 62 | base64 -d mdsBody.b64 | jq . > mdsBody.json 63 | echo -n "let mdsJson=" > js/mds-new.js 64 | cat mdsBody.json >> js/mds-new.js 65 | sed "s+let mdsJson={+{+" js/mds.js | jq -r ".entries[].metadataStatement.description" > prev.authrs 66 | sed "s+let mdsJson={+{+" js/mds-new.js | jq -r ".entries[].metadataStatement.description" > new.authrs 67 | MSG="MDS updated $(diff -U0 prev.authrs new.authrs | sed '1,2d' | sed '/^@@ /d' | sed 's+\"+ +g')" 68 | rm prev.authrs new.authrs 69 | mv js/mds-new.js js/mds.js 70 | echo "const LAST_MDS_UPDATE = '$(date --utc --rfc-2822)';" > js/last-update.js 71 | git config --local user.email "github-actions[bot]@users.noreply.github.com" 72 | git config --local user.name "github-actions[bot]" 73 | git add mds.blob 74 | git add js/mds.js 75 | git add js/last-update.js 76 | git commit -m "$MSG" 77 | git push 78 | else 79 | echo "no MDS change detected" 80 | fi 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser": true, 3 | "strict": "global", 4 | "devel": true, 5 | "esversion": 6 6 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fido-mds-explorer 2 | 3 | Online explorer for the [FIDO Metadata Service v3](https://fidoalliance.org/metadata/) 4 | 5 | You can find the MDS change history [here](https://github.com/opotonniee/fido-mds-explorer/commits/main/js/mds.js) (older changes are [here](https://github.com/opotonniee/fido-mds-explorer/commits/main/mds.js)) 6 | 7 | 8 | ## Browsing 9 | 10 | The [home page](https://opotonniee.github.io/fido-mds-explorer/) lists all the authenticators registered in MDS. 11 | 12 | To only display authenticators with some caracteristics - such as certification level, supported FIDO protocol, or key protection type - click on the column headers and select your criteria. 13 | 14 | Click on an authenticator name to view the authenticator details. 15 | 16 | ## Querying 17 | 18 | If you want to reference a given authenticator through a hyperlink, you can directly open the detailed authenticator page using one of the following URL query parameters: 19 | 20 | - **aaguid**: View the authenticator with the given aaguid. 21 | 22 | Example: https://opotonniee.github.io/fido-mds-explorer/?aaguid=efb96b10-a9ee-4b6c-a4a9-d32125ccd4a4 23 | 24 | - **x5c**: View the authenticator which issuer has the given attestation signature certificate. The certificate must be base64 and URL encoded. 25 | 26 | Example: 27 | https://opotonniee.github.io/fido-mds-explorer/?x5c=MIIECTCCAvGgAwIBAgIMR3MEC%2BUtMnHSFSytMA0GCSqGSIb3DQEBCwUAMGoxCzAJBgNVBAYTAkZSMQ4wDAYDVQQHDAVUb3VyczEQMA4GA1UECgwHR2VtYWx0bzE5MDcGA1UEAwwwR2VtYWx0byBCdXNpbmVzcyBTb2x1dGlvbnMgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTE5MDUwMjE0MzU1M1oXDTI5MDUwMTE0MzY1M1owTjELMAkGA1UEBhMCRlIxEzARBgNVBAoMCkdlbWFsdG8gU0ExDDAKBgNVBAsMA0RJUzEcMBoGA1UEAwwTd3d3LnRoYWxlc2dyb3VwLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK1q3MIB0ekGBbKwZ0FWKMFom1ehsAyEL1UfGXe18ZTyhyVg%2BVchy%2FiH9o3sq0fPCkuDy29iAPjyWWOQTf7qdS1ETq8iRpKNSxxplJkVGnSzLzK%2BxCDx5M4daHJLp88W2JO8HL0Wci8JroNm3Uz7YR%2Bz4UU0apzObdd2lRwEE0mp1TDFJ0jxT%2BxahlzZAldf92%2F%2FsWddoYRrTodc%2FjdefEX9bmgwHNTt3zHBJoP88yoQ12nUKHes3N2%2FqQx3HjA2%2BySFfmdYAqerDej52orvA1V%2FQpd87PV9DBNI0t9tB01t%2B6PbuPojfeqSlNBm1kTqKyU9OKmVw8BwQEXQKmT%2Bt0sCAwEAAaOByjCBxzASBgNVHRMBAf8ECDAGAQH%2FAgEAMB0GA1UdDgQWBBQvMq30QjeHLOzWmDi53KEOg86GjDAfBgNVHSMEGDAWgBR3VfWnLWVDUohSlYrzg23yYaA2bjAOBgNVHQ8BAf8EBAMCAQYwYQYDVR0fBFowWDBWoFSgUoZQaHR0cDovL2NybC1icGtpLmdlbWFsdG8uY29tL0NSTC9HZW1hbHRvQnVzaW5lc3NTb2x1dGlvbnNDZXJ0aWZpY2F0ZUF1dGhvcml0eS5jcmwwDQYJKoZIhvcNAQELBQADggEBAHckIlQopNiBCD6mMSiEg07taoZZNVPLKASv54ZqXofxhIdoqlqts%2FW5NYJ6T%2B%2FFwhn7mSebCKnwuUhaqByVkVt7kheBIw%2FF6aPaAdU8YIcuL8bkvGPvt5oQmU99buUV1pTbrEedU1RYlWLe4Etn6LSiEyKKpsDoBQBHWsJEjgVqHKFeRkQ%2FWgFmGc1%2BwxRyKAGFothrtraw1rerK3p%2BBNy0GRtfMN7tOnTn2giOvtOtebMBCYzyeRl%2F9XALfUC8Mw%2BOoxvc51OE7lhe2yjuO3xF3SjE0ax%2BcWAjGQHhuIuVdfX8CVu%2FR5SG52zA9Oo4yug%2BcjKieAAEu2OPH%2BimIyM%3D 28 | 29 | 30 | If the query does not match any registered authenticator, an error will be displayed and you will be redirected to the home page. -------------------------------------------------------------------------------- /bin/getVendors.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | HTML=vendors.html 4 | CSV=vendors.csv 5 | TMP=entries.tmp 6 | JS=vendors.js 7 | COOKIES=cookies.tmp 8 | 9 | # Download URL from https://fidoalliance.org/certification/functional-certification/vendor-ids/ 10 | 11 | CASPIO_ID=$1 12 | 13 | wget -O $CSV "https://c0ezh785.caspio.com/dp.asp?AppKey=${CASPIO_ID}&downloadFormat=csv&RecordID=&PageID=2&PrevPageID=&cpipage=&download=1&rnd=1712004236605" 14 | 15 | if [ ! -f $CSV ]; then 16 | echo Failed to fetch vendor list 17 | exit 1 18 | fi 19 | head -n1 $CSV | grep Assigned_Vendor_ID 20 | if [ $? -eq 1 ]; then 21 | echo "Vendor list file doesn't look good" 22 | exit 1 23 | fi 24 | 25 | tail -n +2 $CSV | sed -r 's/"([^"]*)","([^"]*)"/ "\2": "\1",/' > $TMP 26 | 27 | if [ ! -f $TMP ]; then 28 | echo Failed to convert file 29 | exit 2 30 | fi 31 | 32 | echo ' 33 | /* 34 | GENERATED FILE 35 | from http://c0ezh785.caspio.com/ 36 | */ 37 | 38 | // eslint-disable-next-line no-unused-vars 39 | const vendors = { 40 | 41 | // extracted' > $JS 42 | 43 | cat $TMP >> $JS 44 | 45 | echo ' 46 | // manual 47 | "0017": "LGE", 48 | "001E": "BTWorks", 49 | "0022": "Movenda", 50 | "0030": "Thales", 51 | "0031": "ATsolutions", 52 | "0063": "VTC SmartTech", 53 | "0064": "SecuGen", 54 | "0066": "Capy", 55 | "006F": "Hanko", 56 | "1EA8": "Excelsecu", 57 | "DAB8": "DDS" 58 | };' >> $JS 59 | 60 | sed -i -e 's/\r//g' $JS 61 | 62 | mv $JS ../js 63 | rm -f $CSV $TMP $JS* 64 | -------------------------------------------------------------------------------- /css/mds-explorer.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin:0; 3 | } 4 | body { 5 | margin: 0; 6 | padding: 15px; 7 | font-family: sans-serif; 8 | background-color: whitesmoke; 9 | } 10 | 11 | h1 { 12 | font-weight: bold; 13 | text-overflow: ellipsis; 14 | vertical-align: middle; 15 | width: 100%; 16 | margin: -15px 0 0 -15px; 17 | padding: 15px; 18 | background-color: lightsteelblue; 19 | border-bottom: 1px solid black; 20 | box-shadow: 0 2px 2px 1px #888; 21 | } 22 | 23 | 24 | h2 { 25 | width: 100%; 26 | margin: 10px 0 10px -5px; 27 | padding: 5px; 28 | background-color: lightgray; 29 | border-bottom: 1px solid black; 30 | border-top: 1px solid rgb(186, 186, 186); 31 | box-shadow: 0 2px 2px 1px #888; 32 | } 33 | 34 | .last-update { 35 | margin: 5px 10px 0 0; 36 | padding: 5px; 37 | font-style: italic; 38 | float:right; 39 | display: none; 40 | } 41 | 42 | button, #file-label { 43 | border: 1px solid #777; 44 | border-radius: 3px; 45 | padding: 3px 8px; 46 | margin: 2px; 47 | background: #E4E4E4; 48 | font-size: 13px; 49 | text-align: center; 50 | } 51 | button:hover, 52 | #file-label:hover { 53 | background-color: #c4e2a5; 54 | } 55 | input[type=text], 56 | input[type=password] { 57 | padding: 1px; 58 | margin: 0; 59 | font-size: 13px; 60 | } 61 | .noselect { 62 | -webkit-touch-callout: none; /* iOS Safari */ 63 | -webkit-user-select: none; /* Safari */ 64 | -khtml-user-select: none; /* Konqueror HTML */ 65 | -moz-user-select: none; /* Old versions of Firefox */ 66 | -ms-user-select: none; /* Internet Explorer/Edge */ 67 | user-select: none; /* Non-prefixed version, currently 68 | supported by Chrome, Edge, Opera and Firefox */ 69 | } 70 | 71 | #mds-loading { 72 | margin: 30px 10px; 73 | font-size: 150%; 74 | } 75 | 76 | button:active { 77 | color: white; 78 | } 79 | #about { 80 | float: right; 81 | } 82 | #about a { 83 | color: white; 84 | text-decoration: none; 85 | } 86 | #about img { 87 | max-width: 36px; 88 | } 89 | 90 | .fold { 91 | color: #1992ca; 92 | } 93 | 94 | .fold:hover { 95 | cursor: pointer; 96 | } 97 | 98 | .clickable, a { 99 | color: #0783bd; 100 | cursor: pointer; 101 | font-weight: bold; 102 | text-decoration: none; 103 | } 104 | 105 | .centered { 106 | text-align: center !important; 107 | } 108 | .disabled { 109 | text-decoration: line-through; 110 | color: gray; 111 | } 112 | 113 | input[type="checkbox"] { 114 | height: 15px; 115 | width: 15px; 116 | } 117 | input[type="checkbox"]:not(:checked) { 118 | background: #fff; 119 | border: 1px solid #ccc; 120 | } 121 | input[type="checkbox"]:checked { 122 | background: black; 123 | color: white; 124 | content: "X"; 125 | } 126 | 127 | /* for 480px or less */ 128 | @media screen and (max-width: 480px) { 129 | h1 { 130 | font-size: 24px; 131 | } 132 | h2 { 133 | font-size: 18px; 134 | } 135 | } 136 | 137 | /* MDS */ 138 | pre { 139 | white-space: pre-wrap; /* css-3 */ 140 | white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ 141 | white-space: -pre-wrap; /* Opera 4-6 */ 142 | white-space: -o-pre-wrap; /* Opera 7 */ 143 | word-wrap: break-word; /* Internet Explorer 5.5+ */ 144 | } 145 | 146 | .vendor { 147 | font-style: italic; 148 | color: #555; 149 | } 150 | footer { 151 | color: grey; 152 | font-size: 80%; 153 | text-align: right; 154 | margin-top: 10px; 155 | } 156 | .tabulator-cell img { 157 | max-width: 100px; 158 | max-height: 50px; 159 | } 160 | .tabulator .tabulator-footer { 161 | text-align: left; 162 | color: #555; 163 | white-space: nowrap; 164 | font-style: italic; 165 | font-weight: initial; 166 | } 167 | #authr-json img { 168 | max-width: 100px; 169 | max-height: 100px; 170 | } 171 | .buffer { 172 | overflow: hidden; 173 | text-overflow: ellipsis; 174 | display: inline-block; 175 | max-width: 95%; 176 | vertical-align: bottom; 177 | } 178 | 179 | ul, ol { 180 | padding-left: 20px; 181 | } 182 | -------------------------------------------------------------------------------- /css/tabulator.min.css: -------------------------------------------------------------------------------- 1 | .tabulator{background-color:#888;border:1px solid #999;font-size:14px;overflow:hidden;position:relative;text-align:left;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.tabulator[tabulator-layout=fitDataFill] .tabulator-tableholder .tabulator-table{min-width:100%}.tabulator[tabulator-layout=fitDataTable]{display:inline-block}.tabulator.tabulator-block-select,.tabulator.tabulator-ranges .tabulator-cell:not(.tabulator-editing){user-select:none}.tabulator .tabulator-header{background-color:#e6e6e6;border-bottom:1px solid #999;box-sizing:border-box;color:#555;font-weight:700;outline:none;overflow:hidden;position:relative;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;white-space:nowrap;width:100%}.tabulator .tabulator-header.tabulator-header-hidden{display:none}.tabulator .tabulator-header .tabulator-header-contents{overflow:hidden;position:relative}.tabulator .tabulator-header .tabulator-header-contents .tabulator-headers{display:inline-block}.tabulator .tabulator-header .tabulator-col{background:#e6e6e6;border-right:1px solid #aaa;box-sizing:border-box;display:inline-flex;flex-direction:column;justify-content:flex-start;overflow:hidden;position:relative;text-align:left;vertical-align:bottom}.tabulator .tabulator-header .tabulator-col.tabulator-moving{background:#cdcdcd;border:1px solid #999;pointer-events:none;position:absolute}.tabulator .tabulator-header .tabulator-col.tabulator-range-highlight{background-color:#d6d6d6;color:#000}.tabulator .tabulator-header .tabulator-col.tabulator-range-selected{background-color:#3876ca;color:#fff}.tabulator .tabulator-header .tabulator-col .tabulator-col-content{box-sizing:border-box;padding:4px;position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-header-popup-button{padding:0 8px}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-header-popup-button:hover{cursor:pointer;opacity:.6}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title-holder{position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title{box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;vertical-align:bottom;white-space:nowrap;width:100%}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title.tabulator-col-title-wrap{text-overflow:clip;white-space:normal}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-title-editor{background:#fff;border:1px solid #999;box-sizing:border-box;padding:1px;width:100%}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-header-popup-button+.tabulator-title-editor{width:calc(100% - 22px)}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter{align-items:center;bottom:0;display:flex;position:absolute;right:4px;top:0}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #bbb;border-left:6px solid transparent;border-right:6px solid transparent;height:0;width:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols{border-top:1px solid #aaa;display:flex;margin-right:-1px;overflow:hidden;position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter{box-sizing:border-box;margin-top:2px;position:relative;text-align:center;width:100%}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter textarea{height:auto!important}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter svg{margin-top:3px}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input::-ms-clear{height:0;width:0}.tabulator .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title{padding-right:25px}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable.tabulator-col-sorter-element:hover{background-color:#cdcdcd;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter{color:#bbb}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{border-bottom:6px solid #555;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #bbb;border-top:none}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter{color:#666}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{border-bottom:6px solid #555;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #666;border-top:none}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter{color:#666}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{border-top:6px solid #555;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:none;border-top:6px solid #666;color:#666}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical .tabulator-col-content .tabulator-col-title{align-items:center;display:flex;justify-content:center;text-orientation:mixed;writing-mode:vertical-rl}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-col-vertical-flip .tabulator-col-title{transform:rotate(180deg)}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-title{padding-right:0;padding-top:20px}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-vertical-flip .tabulator-col-title{padding-bottom:20px;padding-right:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-sorter{bottom:auto;justify-content:center;left:0;right:0;top:4px}.tabulator .tabulator-header .tabulator-frozen{left:0;position:sticky;z-index:11}.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left{border-right:2px solid #aaa}.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-right{border-left:2px solid #aaa}.tabulator .tabulator-header .tabulator-calcs-holder{background:#f3f3f3!important;border-bottom:1px solid #aaa;border-top:1px solid #aaa;box-sizing:border-box;display:inline-block}.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row{background:#f3f3f3!important}.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle{display:none}.tabulator .tabulator-header .tabulator-frozen-rows-holder{display:inline-block}.tabulator .tabulator-header .tabulator-frozen-rows-holder:empty{display:none}.tabulator .tabulator-tableholder{-webkit-overflow-scrolling:touch;overflow:auto;position:relative;white-space:nowrap;width:100%}.tabulator .tabulator-tableholder:focus{outline:none}.tabulator .tabulator-tableholder .tabulator-placeholder{align-items:center;box-sizing:border-box;display:flex;justify-content:center;min-width:100%;width:100%}.tabulator .tabulator-tableholder .tabulator-placeholder[tabulator-render-mode=virtual]{min-height:100%}.tabulator .tabulator-tableholder .tabulator-placeholder .tabulator-placeholder-contents{color:#ccc;display:inline-block;font-size:20px;font-weight:700;padding:10px;text-align:center;white-space:normal}.tabulator .tabulator-tableholder .tabulator-table{background-color:#fff;color:#333;display:inline-block;overflow:visible;position:relative;white-space:nowrap}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs{background:#e2e2e2!important;font-weight:700}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-top{border-bottom:2px solid #aaa}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-bottom{border-top:2px solid #aaa}.tabulator .tabulator-tableholder .tabulator-range-overlay{inset:0;pointer-events:none;position:absolute;z-index:10}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range{border:1px solid #2975dd;box-sizing:border-box;position:absolute}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range.tabulator-range-active:after{background-color:#2975dd;border-radius:999px;bottom:-3px;content:"";height:6px;position:absolute;right:-3px;width:6px}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range-cell-active{border:2px solid #2975dd;box-sizing:border-box;position:absolute}.tabulator .tabulator-footer{background-color:#e6e6e6;border-top:1px solid #999;color:#555;font-weight:700;user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;white-space:nowrap}.tabulator .tabulator-footer .tabulator-footer-contents{align-items:center;display:flex;flex-direction:row;justify-content:space-between;padding:5px 10px}.tabulator .tabulator-footer .tabulator-footer-contents:empty{display:none}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs{margin-top:-5px;overflow-x:auto}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab{border:1px solid #999;border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-top:none;display:inline-block;font-size:.9em;padding:5px}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab:hover{cursor:pointer;opacity:.7}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab.tabulator-spreadsheet-tab-active{background:#fff}.tabulator .tabulator-footer .tabulator-calcs-holder{background:#f3f3f3!important;border-bottom:1px solid #aaa;border-top:1px solid #aaa;box-sizing:border-box;overflow:hidden;text-align:left;width:100%}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row{background:#f3f3f3!important;display:inline-block}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle{display:none}.tabulator .tabulator-footer .tabulator-calcs-holder:only-child{border-bottom:none;margin-bottom:-5px}.tabulator .tabulator-footer>*+.tabulator-page-counter{margin-left:10px}.tabulator .tabulator-footer .tabulator-page-counter{font-weight:400}.tabulator .tabulator-footer .tabulator-paginator{color:#555;flex:1;font-family:inherit;font-size:inherit;font-weight:inherit;text-align:right}.tabulator .tabulator-footer .tabulator-page-size{border:1px solid #aaa;border-radius:3px;display:inline-block;margin:0 5px;padding:2px 5px}.tabulator .tabulator-footer .tabulator-pages{margin:0 7px}.tabulator .tabulator-footer .tabulator-page{background:hsla(0,0%,100%,.2);border:1px solid #aaa;border-radius:3px;display:inline-block;margin:0 2px;padding:2px 5px}.tabulator .tabulator-footer .tabulator-page.active{color:#d00}.tabulator .tabulator-footer .tabulator-page:disabled{opacity:.5}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-footer .tabulator-page:not(disabled):hover{background:rgba(0,0,0,.2);color:#fff;cursor:pointer}}.tabulator .tabulator-col-resize-handle{display:inline-block;margin-left:-3px;margin-right:-3px;position:relative;vertical-align:middle;width:6px;z-index:11}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-col-resize-handle:hover{cursor:ew-resize}}.tabulator .tabulator-col-resize-handle:last-of-type{margin-right:0;width:3px}.tabulator .tabulator-col-resize-guide{background-color:#999;height:100%;margin-left:-.5px;opacity:.5;position:absolute;top:0;width:4px}.tabulator .tabulator-row-resize-guide{background-color:#999;height:4px;left:0;margin-top:-.5px;opacity:.5;position:absolute;width:100%}.tabulator .tabulator-alert{align-items:center;background:rgba(0,0,0,.4);display:flex;height:100%;left:0;position:absolute;text-align:center;top:0;width:100%;z-index:100}.tabulator .tabulator-alert .tabulator-alert-msg{background:#fff;border-radius:10px;display:inline-block;font-size:16px;font-weight:700;margin:0 auto;padding:10px 20px}.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-msg{border:4px solid #333;color:#000}.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-error{border:4px solid #d00;color:#590000}.tabulator-row{background-color:#fff;box-sizing:border-box;min-height:22px;position:relative}.tabulator-row.tabulator-row-even{background-color:#efefef}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selectable:hover{background-color:#bbb;cursor:pointer}}.tabulator-row.tabulator-selected{background-color:#9abcea}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selected:hover{background-color:#769bcc;cursor:pointer}}.tabulator-row.tabulator-row-moving{background:#fff;border:1px solid #000}.tabulator-row.tabulator-moving{border-bottom:1px solid #aaa;border-top:1px solid #aaa;pointer-events:none;position:absolute;z-index:15}.tabulator-row.tabulator-range-highlight .tabulator-cell.tabulator-range-row-header{background-color:#d6d6d6;color:#000}.tabulator-row.tabulator-range-highlight.tabulator-range-selected .tabulator-cell.tabulator-range-row-header,.tabulator-row.tabulator-range-selected .tabulator-cell.tabulator-range-row-header{background-color:#3876ca;color:#fff}.tabulator-row .tabulator-row-resize-handle{bottom:0;height:5px;left:0;position:absolute;right:0}.tabulator-row .tabulator-row-resize-handle.prev{bottom:auto;top:0}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-row-resize-handle:hover{cursor:ns-resize}}.tabulator-row .tabulator-responsive-collapse{border-bottom:1px solid #aaa;border-top:1px solid #aaa;box-sizing:border-box;padding:5px}.tabulator-row .tabulator-responsive-collapse:empty{display:none}.tabulator-row .tabulator-responsive-collapse table{font-size:14px}.tabulator-row .tabulator-responsive-collapse table tr td{position:relative}.tabulator-row .tabulator-responsive-collapse table tr td:first-of-type{padding-right:10px}.tabulator-row .tabulator-cell{border-right:1px solid #aaa;box-sizing:border-box;display:inline-block;outline:none;overflow:hidden;padding:4px;position:relative;text-overflow:ellipsis;vertical-align:middle;white-space:nowrap}.tabulator-row .tabulator-cell.tabulator-row-header{background:#e6e6e6;border-bottom:1px solid #aaa;border-right:1px solid #999}.tabulator-row .tabulator-cell.tabulator-frozen{background-color:inherit;display:inline-block;left:0;position:sticky;z-index:11}.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left{border-right:2px solid #aaa}.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right{border-left:2px solid #aaa}.tabulator-row .tabulator-cell.tabulator-editing{border:1px solid #1d68cd;outline:none;padding:0}.tabulator-row .tabulator-cell.tabulator-editing input,.tabulator-row .tabulator-cell.tabulator-editing select{background:transparent;border:1px;outline:none}.tabulator-row .tabulator-cell.tabulator-validation-fail{border:1px solid #d00}.tabulator-row .tabulator-cell.tabulator-validation-fail input,.tabulator-row .tabulator-cell.tabulator-validation-fail select{background:transparent;border:1px;color:#d00}.tabulator-row .tabulator-cell.tabulator-row-handle{align-items:center;display:inline-flex;justify-content:center;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none}.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box{width:80%}.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box .tabulator-row-handle-bar{background:#666;height:3px;margin-top:2px;width:100%}.tabulator-row .tabulator-cell.tabulator-range-selected:not(.tabulator-range-only-cell-selected):not(.tabulator-range-row-header){background-color:#9abcea}.tabulator-row .tabulator-cell .tabulator-data-tree-branch-empty{display:inline-block;width:7px}.tabulator-row .tabulator-cell .tabulator-data-tree-branch{border-bottom:2px solid #aaa;border-bottom-left-radius:1px;border-left:2px solid #aaa;display:inline-block;height:9px;margin-right:5px;margin-top:-9px;vertical-align:middle;width:7px}.tabulator-row .tabulator-cell .tabulator-data-tree-control{align-items:center;background:rgba(0,0,0,.1);border:1px solid #333;border-radius:2px;display:inline-flex;height:11px;justify-content:center;margin-right:5px;overflow:hidden;vertical-align:middle;width:11px}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-cell .tabulator-data-tree-control:hover{background:rgba(0,0,0,.2);cursor:pointer}}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse{background:transparent;display:inline-block;height:7px;position:relative;width:1px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after{background:#333;content:"";height:1px;left:-3px;position:absolute;top:3px;width:7px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand{background:#333;display:inline-block;height:7px;position:relative;width:1px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand:after{background:#333;content:"";height:1px;left:-3px;position:absolute;top:3px;width:7px}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle{align-items:center;background:#666;border-radius:20px;color:#fff;display:inline-flex;font-size:1.1em;font-weight:700;height:15px;justify-content:center;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;width:15px}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle:hover{cursor:pointer;opacity:.7}}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-close{display:initial}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-open{display:none}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle svg{stroke:#fff}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle .tabulator-responsive-collapse-toggle-close{display:none}.tabulator-row .tabulator-cell .tabulator-traffic-light{border-radius:14px;display:inline-block;height:14px;width:14px}.tabulator-row.tabulator-group{background:#ccc;border-bottom:1px solid #999;border-right:1px solid #aaa;border-top:1px solid #999;box-sizing:border-box;font-weight:700;min-width:100%;padding:5px 5px 5px 10px}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-group:hover{background-color:rgba(0,0,0,.1);cursor:pointer}}.tabulator-row.tabulator-group.tabulator-group-visible .tabulator-arrow{border-bottom:0;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #666;margin-right:10px}.tabulator-row.tabulator-group.tabulator-group-level-1{padding-left:30px}.tabulator-row.tabulator-group.tabulator-group-level-2{padding-left:50px}.tabulator-row.tabulator-group.tabulator-group-level-3{padding-left:70px}.tabulator-row.tabulator-group.tabulator-group-level-4{padding-left:90px}.tabulator-row.tabulator-group.tabulator-group-level-5{padding-left:110px}.tabulator-row.tabulator-group .tabulator-group-toggle{display:inline-block}.tabulator-row.tabulator-group .tabulator-arrow{border-bottom:6px solid transparent;border-left:6px solid #666;border-right:0;border-top:6px solid transparent;display:inline-block;height:0;margin-right:16px;vertical-align:middle;width:0}.tabulator-row.tabulator-group span{color:#d00;margin-left:10px}.tabulator-toggle{background:#dcdcdc;border:1px solid #ccc;box-sizing:border-box;display:flex;flex-direction:row}.tabulator-toggle.tabulator-toggle-on{background:#1c6cc2}.tabulator-toggle .tabulator-toggle-switch{background:#fff;border:1px solid #ccc;box-sizing:border-box}.tabulator-popup-container{-webkit-overflow-scrolling:touch;background:#fff;border:1px solid #aaa;box-shadow:0 0 5px 0 rgba(0,0,0,.2);box-sizing:border-box;display:inline-block;font-size:14px;overflow-y:auto;position:absolute;z-index:10000}.tabulator-popup{border-radius:3px;padding:5px}.tabulator-tooltip{border-radius:2px;box-shadow:none;font-size:12px;max-width:Min(500px,100%);padding:3px 5px;pointer-events:none}.tabulator-menu .tabulator-menu-item{box-sizing:border-box;padding:5px 10px;position:relative;user-select:none}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-disabled{opacity:.5}@media (hover:hover) and (pointer:fine){.tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled):hover{background:#efefef;cursor:pointer}}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu{padding-right:25px}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu:after{border-color:#aaa;border-style:solid;border-width:1px 1px 0 0;content:"";display:inline-block;height:7px;position:absolute;right:10px;top:calc(5px + .4em);transform:rotate(45deg);vertical-align:top;width:7px}.tabulator-menu .tabulator-menu-separator{border-top:1px solid #aaa}.tabulator-edit-list{-webkit-overflow-scrolling:touch;font-size:14px;max-height:200px;overflow-y:auto}.tabulator-edit-list .tabulator-edit-list-item{color:#333;outline:none;padding:4px}.tabulator-edit-list .tabulator-edit-list-item.active{background:#1d68cd;color:#fff}.tabulator-edit-list .tabulator-edit-list-item.active.focused{outline:1px solid hsla(0,0%,100%,.5)}.tabulator-edit-list .tabulator-edit-list-item.focused{outline:1px solid #1d68cd}@media (hover:hover) and (pointer:fine){.tabulator-edit-list .tabulator-edit-list-item:hover{background:#1d68cd;color:#fff;cursor:pointer}}.tabulator-edit-list .tabulator-edit-list-placeholder{color:#333;padding:4px;text-align:center}.tabulator-edit-list .tabulator-edit-list-group{border-bottom:1px solid #aaa;color:#333;font-weight:700;padding:6px 4px 4px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-2,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-2{padding-left:12px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-3,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-3{padding-left:20px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-4,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-4{padding-left:28px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-5,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-5{padding-left:36px}.tabulator.tabulator-ltr{direction:ltr}.tabulator.tabulator-rtl{direction:rtl;text-align:initial}.tabulator.tabulator-rtl .tabulator-header .tabulator-col{border-left:1px solid #aaa;border-right:initial;text-align:initial}.tabulator.tabulator-rtl .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols{margin-left:-1px;margin-right:0}.tabulator.tabulator-rtl .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title{padding-left:25px;padding-right:0}.tabulator.tabulator-rtl .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter{left:8px;right:auto}.tabulator.tabulator-rtl .tabulator-tableholder .tabulator-range-overlay .tabulator-range.tabulator-range-active:after{background-color:#2975dd;border-radius:999px;bottom:-3px;content:"";height:6px;left:-3px;position:absolute;right:auto;width:6px}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell{border-left:1px solid #aaa;border-right:initial}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell .tabulator-data-tree-branch{border-bottom-left-radius:0;border-bottom-right-radius:1px;border-left:initial;border-right:2px solid #aaa;margin-left:5px;margin-right:0}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell .tabulator-data-tree-control{margin-left:5px;margin-right:0}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left{border-left:2px solid #aaa}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right{border-right:2px solid #aaa}.tabulator.tabulator-rtl .tabulator-row .tabulator-col-resize-handle:last-of-type{margin-left:0;margin-right:-3px;width:3px}.tabulator.tabulator-rtl .tabulator-footer .tabulator-calcs-holder{text-align:initial}.tabulator-print-fullscreen{bottom:0;left:0;position:absolute;right:0;top:0;z-index:10000}body.tabulator-print-fullscreen-hide>:not(.tabulator-print-fullscreen){display:none!important}.tabulator-print-table{border-collapse:collapse}.tabulator-print-table .tabulator-data-tree-branch{border-bottom:2px solid #aaa;border-bottom-left-radius:1px;border-left:2px solid #aaa;display:inline-block;height:9px;margin-right:5px;margin-top:-9px;vertical-align:middle;width:7px}.tabulator-print-table .tabulator-print-table-group{background:#ccc;border-bottom:1px solid #999;border-right:1px solid #aaa;border-top:1px solid #999;box-sizing:border-box;font-weight:700;min-width:100%;padding:5px 5px 5px 10px}@media (hover:hover) and (pointer:fine){.tabulator-print-table .tabulator-print-table-group:hover{background-color:rgba(0,0,0,.1);cursor:pointer}}.tabulator-print-table .tabulator-print-table-group.tabulator-group-visible .tabulator-arrow{border-bottom:0;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #666;margin-right:10px}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-1 td{padding-left:30px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-2 td{padding-left:50px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-3 td{padding-left:70px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-4 td{padding-left:90px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-5 td{padding-left:110px!important}.tabulator-print-table .tabulator-print-table-group .tabulator-group-toggle{display:inline-block}.tabulator-print-table .tabulator-print-table-group .tabulator-arrow{border-bottom:6px solid transparent;border-left:6px solid #666;border-right:0;border-top:6px solid transparent;display:inline-block;height:0;margin-right:16px;vertical-align:middle;width:0}.tabulator-print-table .tabulator-print-table-group span{color:#d00;margin-left:10px}.tabulator-print-table .tabulator-data-tree-control{align-items:center;background:rgba(0,0,0,.1);border:1px solid #333;border-radius:2px;display:inline-flex;height:11px;justify-content:center;margin-right:5px;overflow:hidden;vertical-align:middle;width:11px}@media (hover:hover) and (pointer:fine){.tabulator-print-table .tabulator-data-tree-control:hover{background:rgba(0,0,0,.2);cursor:pointer}}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-collapse{background:transparent;display:inline-block;height:7px;position:relative;width:1px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after{background:#333;content:"";height:1px;left:-3px;position:absolute;top:3px;width:7px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-expand{background:#333;display:inline-block;height:7px;position:relative;width:1px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-expand:after{background:#333;content:"";height:1px;left:-3px;position:absolute;top:3px;width:7px} 2 | /*# sourceMappingURL=tabulator.min.css.map */ -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opotonniee/fido-mds-explorer/7ff3d59edfdb63699377dd3c9082f533a9a2c320/favicon.png -------------------------------------------------------------------------------- /img/github-mark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | FIDO MDS Explorer 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 24 | 25 | 26 |

27 | FIDO MDS Explorer 28 | 29 | 30 | github icon 31 | 32 | 33 |

34 | 35 |
Loading...
36 | 40 | 41 | 53 | 54 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /js/last-update.js: -------------------------------------------------------------------------------- 1 | const LAST_MDS_UPDATE = 'Sun, 01 Jun 2025 00:55:34 +0000'; 2 | -------------------------------------------------------------------------------- /js/mds-explorer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* globals x509, mdsJson, Tabulator, LAST_MDS_UPDATE, vendors */ 3 | 4 | let table; 5 | let cert; 6 | 7 | function type(obj) { 8 | return Object.prototype.toString.call(obj).match(/.* (.*)\]/)[1]; 9 | } 10 | 11 | function getVendor(aaid) { 12 | let vendorId = aaid ? aaid.substring(0,4) : undefined; 13 | let vendorName = vendorId ? vendors[vendorId.toUpperCase()] : undefined; 14 | return vendorName ? vendorName : "??"; 15 | } 16 | 17 | function imageTag(src) { 18 | return src ? "" : ""; 19 | } 20 | 21 | // --- DOM/JS helper 22 | 23 | function e(selector) { 24 | return document.querySelector(selector); 25 | } 26 | 27 | function newE(tag, attributes, html) { 28 | let el = document.createElement(tag); 29 | for (let a in attributes) { 30 | el.setAttribute(a, attributes[a]); 31 | } 32 | el.innerHTML = html; 33 | return el; 34 | } 35 | 36 | const ready = (callback) => { 37 | if (document.readyState != "loading") callback(); 38 | else document.addEventListener("DOMContentLoaded", callback); 39 | } 40 | 41 | const cpy = typeof navigator.clipboard?.writeText === "function" ? 42 | "📋" : ""; 43 | 44 | 45 | // --- 46 | 47 | function certificate(obj) { 48 | let certHtml = ""; 75 | return certHtml; 76 | } 77 | 78 | function stringify(obj) { 79 | if (type(obj) === "Function") { 80 | return "function"; 81 | } else if (type(obj) === "Undefined") { 82 | return "undefined"; 83 | } else if (type(obj) === "Null") { 84 | return "null"; 85 | } else if (type(obj) === "Number") { 86 | return obj; 87 | } else if (type(obj) === "String") { 88 | return '"' + obj + '"'; 89 | } else if (type(obj) === "Date") { 90 | return '"' + obj.toGMTString() + '"'; 91 | } else if (type(obj) === "Array") { 92 | return '
    ' + obj.map(function (o) { return "
  1. " + stringify(o) + "
  2. \n"; }).join("") + '
\n'; 93 | } else if (type(obj) === "ArrayBuffer") { 94 | return cpy + " " + [...new Uint8Array(obj)].map(x => x.toString(16).padStart(2, '0')).join('') + ""; 95 | } else if (type(obj) === "Object") { 96 | var result = []; 97 | Object.keys(obj).forEach(function (key) { 98 | let val; 99 | 100 | // specific decodings: 101 | 102 | // Icon 103 | if (["icon", "iconDark", "providerLogoLight", "providerLogoDark"].includes(key)) { 104 | val = imageTag(obj[key]); 105 | 106 | // Certificates 107 | } else if (key == "attestationRootCertificates") { 108 | val = '
    ' + obj[key].map(function (o) { return "
  1. " + certificate(o) + "
  2. "; }).join("") + '
\n'; 109 | } else if ((key == "certificate") || (key == "batchCertificate")) { 110 | val = certificate(obj[key]) + '\n'; 111 | 112 | // AAID 113 | } else if (key == "aaid") { 114 | val = '"' + obj[key] + '" (' + getVendor(obj[key]) + ')'; 115 | 116 | // generic decoding 117 | } else { 118 | val = stringify(obj[key]); 119 | } 120 | 121 | if (val !== null) { 122 | result.push('
  • ' + key + ': ' + val + "
  • \n"); 123 | } 124 | }); 125 | return "\n"; 126 | } 127 | } 128 | 129 | 130 | function showAuthr(json) { 131 | e("#mds").hidden = true; 132 | e("#authr").hidden = false; 133 | e("#authr-name").innerText = json.metadataStatement.description; 134 | e("#authr-json").innerHTML = stringify(json); 135 | for (let item of document.querySelectorAll(".cpy")) { 136 | item.addEventListener("click",(event) => { 137 | const COPIED = "Copied to clipboard..."; 138 | let elt = event.target.parentNode.querySelector(".buffer"); 139 | let v = elt.textContent; 140 | if (v != COPIED) { 141 | elt.textContent = COPIED; 142 | navigator.clipboard.writeText(v); 143 | setTimeout(() => { 144 | elt.textContent = v; 145 | }, 2000); 146 | } 147 | }); 148 | } 149 | } 150 | 151 | function clickAuthr(e, cell) { 152 | showAuthr(cell.getData()); 153 | history.pushState({"authr": cell.getData()}, "View", "#view"); 154 | } 155 | 156 | e("#authr-close").addEventListener("click", function() { 157 | history.back(); 158 | }); 159 | 160 | function isMatchingFilter(headerValue, values) { 161 | if (Array.isArray(headerValue) && headerValue.length == 1) { 162 | // array with single value, use it 163 | headerValue = headerValue[0]; 164 | } 165 | return Array.isArray(headerValue) || // if multiple values (i.e. all): no filter 166 | (headerValue == "") || // empty: no filter 167 | values.includes(headerValue); // ? 168 | } 169 | 170 | function filterCertifs(headerValue, rowValue/*, rowData, filterParams*/) { 171 | let values = []; 172 | for (let val of rowValue) { 173 | values.push(val.status); 174 | } 175 | return isMatchingFilter(headerValue, values); 176 | } 177 | 178 | function filterIds(headerValue, rowValue/*, rowData, filterParams*/) { 179 | let res; 180 | if (rowValue.protocolFamily == "fido2") { 181 | res = isMatchingFilter(headerValue, rowValue.aaguid); 182 | } else if (rowValue.protocolFamily == "uaf") { 183 | res = isMatchingFilter(headerValue, rowValue.aaid); 184 | } else if (rowValue.protocolFamily == "u2f") { 185 | res = isMatchingFilter(headerValue, rowValue.attestationCertificateKeyIdentifiers); 186 | } 187 | return res; 188 | } 189 | 190 | function filterUserVerifs(headerValue, rowValue/*, rowData, filterParams*/) { 191 | let values = []; 192 | for (let val1 of rowValue) { 193 | for (let val2 of val1) { 194 | values.push(val2.userVerificationMethod); 195 | } 196 | } 197 | return isMatchingFilter(headerValue, values); 198 | } 199 | 200 | ready(() => { 201 | 202 | console.log(mdsJson); 203 | 204 | e("#mds-loading").hidden = true; 205 | if (LAST_MDS_UPDATE) { 206 | e("#last-update-date").innerText = LAST_MDS_UPDATE; 207 | e(".last-update").hidden = false; 208 | } 209 | e("#mds").hidden = false; 210 | // build authenticators table 211 | let hideMenu = [ 212 | { 213 | label: "Hide Column", 214 | action: function (e, column) { 215 | column.hide(); 216 | } 217 | } 218 | ]; 219 | 220 | // Fill filtering drop downs with entries's values 221 | let statuses = [], 222 | protocols = [], 223 | uvs = [], 224 | attachments = [], 225 | transports = [], 226 | kprots = [], 227 | algos = []; 228 | for (let e of mdsJson.entries) { 229 | for (let s of e.statusReports) { 230 | if (s.status && !statuses.includes(s.status)) { 231 | statuses.push(s.status); 232 | } 233 | } 234 | if (!protocols.includes(e.metadataStatement.protocolFamily)) { 235 | protocols.push(e.metadataStatement.protocolFamily); 236 | } 237 | for (let uv of e.metadataStatement.userVerificationDetails || []) { 238 | for (let uvd of uv || []) { 239 | if (uvd?.userVerificationMethod && !uvs.includes(uvd.userVerificationMethod)) { 240 | uvs.push(uvd.userVerificationMethod); 241 | } 242 | } 243 | } 244 | for (let a of e?.metadataStatement?.attachmentHint || []) { 245 | if (!attachments.includes(a)) { 246 | attachments.push(a); 247 | } 248 | } 249 | for (let t of e?.metadataStatement?.authenticatorGetInfo?.transports || []) { 250 | if (!transports.includes(t)) { 251 | transports.push(t); 252 | } 253 | } 254 | for (let kp of e?.metadataStatement?.keyProtection || []) { 255 | if (!kprots.includes(kp)) { 256 | kprots.push(kp); 257 | } 258 | } 259 | for (let alg of e?.metadataStatement?.authenticationAlgorithms || []) { 260 | if (!algos.includes(alg)) { 261 | algos.push(alg); 262 | } 263 | } 264 | } 265 | for (let a of [ statuses, protocols, uvs, attachments, transports, kprots, algos ]) a.sort(); 266 | 267 | table = new Tabulator("#mds-table", { 268 | data: mdsJson.entries, 269 | layout: "fitData", 270 | selectable: false, 271 | movableColumns: true, 272 | //responsiveLayout: "collapse", 273 | columns: [ 274 | { 275 | title: "Name", 276 | field: "metadataStatement.description", 277 | sorter: "string", 278 | headerFilter: true, 279 | formatter: function(cell/*, formatterParams, onRendered*/) { 280 | let name = cell.getValue(); 281 | return `${name}`; 282 | }, 283 | maxWidth: 350, 284 | cellClick: clickAuthr 285 | }, 286 | { 287 | title: "Protocol", 288 | field: "metadataStatement.protocolFamily", 289 | sorter: "string", 290 | headerFilter: "list", 291 | headerFilterParams: { values: protocols }, 292 | headerMenu: hideMenu 293 | }, 294 | { 295 | title: "Icon", 296 | field: "metadataStatement.icon", 297 | formatter: function(cell/*, formatterParams, onRendered*/){ 298 | return imageTag(cell.getValue()); 299 | }, 300 | headerMenu: hideMenu 301 | }, 302 | { 303 | title: "Certification", 304 | field: "statusReports", 305 | formatter: function(cell/*, formatterParams, onRendered*/){ 306 | let res = "", sep=""; 307 | for (let value of cell.getValue() || []) { res += sep + value.status; sep ="
    "; } 308 | return res; 309 | }, 310 | headerFilter: "list", 311 | headerFilterParams: { values: statuses }, 312 | sorter: "array", 313 | headerFilterFunc: filterCertifs, 314 | headerMenu: hideMenu 315 | }, 316 | { 317 | title: "ID (see popup)", 318 | field: "metadataStatement", 319 | sorter: "string", 320 | headerFilter: true, 321 | tooltip: function(e, cell){ 322 | let 323 | item = cell.getValue(), 324 | map = { 325 | fido2: "aaguid", 326 | u2f: "attestationCertificateKeyIdentifiers", 327 | uaf: "aaid" 328 | }, 329 | field = map[item.protocolFamily]; 330 | return field ? field : "?"; 331 | }, 332 | formatter: function(cell/*, formatterParams, onRendered*/){ 333 | let 334 | item = cell.getValue(), 335 | sep ="", 336 | res = "?"; 337 | if (item.protocolFamily == "fido2") { 338 | res = item.aaguid; 339 | } else if (item.protocolFamily == "uaf") { 340 | res = item.aaid; 341 | } else if (item.protocolFamily == "u2f") { 342 | res = ""; 343 | for (let value of item.attestationCertificateKeyIdentifiers) { 344 | res += sep + value; sep ="
    "; 345 | } 346 | } 347 | return res; 348 | }, 349 | headerFilterFunc: filterIds, 350 | headerMenu: hideMenu 351 | }, 352 | { 353 | title: "User Verif.", 354 | field: "metadataStatement.userVerificationDetails", 355 | formatter: function(cell/*, formatterParams, onRendered*/){ 356 | let res = "", sep=""; 357 | for (let line of cell.getValue() || []) { 358 | for (let value of line) { 359 | res += sep + value.userVerificationMethod; sep ="
    "; 360 | } 361 | } 362 | return res; 363 | }, 364 | headerFilter: "list", 365 | headerFilterParams: { values: uvs }, 366 | headerFilterFunc: filterUserVerifs, 367 | headerMenu: hideMenu, 368 | visible: false, 369 | sorter: "array" 370 | }, 371 | { 372 | title: "Attachment", 373 | field: "metadataStatement.attachmentHint", 374 | formatter: function(cell/*, formatterParams, onRendered*/) { 375 | let res = "", sep=""; 376 | for (let value of cell.getValue() || []) { res += sep + value; sep ="
    "; } 377 | return res; 378 | }, 379 | headerFilter: "list", 380 | headerFilterParams: { values: attachments }, 381 | headerMenu: hideMenu, 382 | visible: false, 383 | sorter: "array" 384 | }, 385 | { 386 | title: "Transports", 387 | field: "metadataStatement.authenticatorGetInfo.transports", 388 | formatter: function(cell/*, formatterParams, onRendered*/) { 389 | let res = "", sep=""; 390 | for (let value of cell.getValue() || []) { res += sep + value; sep ="
    "; } 391 | return res; 392 | }, 393 | headerFilter: "list", 394 | headerFilterParams: { values: transports }, 395 | headerMenu: hideMenu, 396 | visible: false, 397 | sorter: "array" 398 | }, 399 | { 400 | title: "Key Protection", 401 | field: "metadataStatement.keyProtection", 402 | formatter: function(cell/*, formatterParams, onRendered*/) { 403 | let res = "", sep=""; 404 | for (let value of cell.getValue() || []) { res += sep + value; sep ="
    "; } 405 | return res; 406 | }, 407 | headerFilter: "list", 408 | headerFilterParams: { values: kprots }, 409 | headerMenu: hideMenu, 410 | visible: false, 411 | sorter: "array" 412 | }, 413 | { 414 | title: "Algorithms", 415 | field: "metadataStatement.authenticationAlgorithms", 416 | formatter: function(cell/*, formatterParams, onRendered*/) { 417 | let res = "", sep=""; 418 | for (let value of cell.getValue() || []) { res += sep + value; sep ="
    "; } 419 | return res; 420 | }, 421 | headerFilter: "list", 422 | headerFilterParams: { values: algos }, 423 | headerMenu: hideMenu, 424 | visible: false, 425 | sorter: "array" 426 | }, 427 | { 428 | title: "Updated", 429 | field: "timeOfLastStatusChange", 430 | headerMenu: hideMenu, 431 | sorter: "string" 432 | } 433 | ], 434 | footerElement: `Payload serial: ${mdsJson.no} - Next update planned on ${mdsJson.nextUpdate} - ${mdsJson.legalHeader}` 435 | }); 436 | 437 | e("#table-size").innerText = mdsJson.entries.length; 438 | table.on("dataFiltered", function (filters, rows) { 439 | e("#table-size").innerText = rows.length; 440 | }); 441 | 442 | function showColumnsSelector(show) { 443 | e("#shown-columns").disabled = !show; 444 | } 445 | 446 | window.addEventListener('popstate', (event) => { 447 | if (e("#authr").checkVisibility()) { 448 | e("#mds").hidden = false; 449 | e("#authr").hidden = true; 450 | } else if (event.state && event.state.authr) { 451 | showAuthr(event.state.authr); 452 | } 453 | }); 454 | 455 | let searchedAuthr; 456 | if (location.search) { 457 | let params = new URLSearchParams(location.search); 458 | if (params.has("aaguid")) { 459 | let aaguid = params.get("aaguid"); 460 | for (const entry of mdsJson.entries) { 461 | if (entry.aaguid == aaguid) { 462 | searchedAuthr = entry; 463 | break; 464 | } 465 | } 466 | } else if (params.has("x5c")) { 467 | let x5c = params.get("x5c"); 468 | for (const entry of mdsJson.entries) { 469 | if (entry.metadataStatement.attestationRootCertificates.includes(x5c)) { 470 | searchedAuthr = entry; 471 | break; 472 | } 473 | } 474 | } 475 | if (searchedAuthr) { 476 | showAuthr(searchedAuthr); 477 | } else { 478 | alert("The authenticator your are looking for was not found"); 479 | window.location = "." 480 | } 481 | } else { 482 | // refresh so that icons column is properly sized 483 | table.on("tableBuilt", function() { 484 | table.redraw(true); 485 | 486 | let hidableColumns = []; 487 | 488 | for (let c of table.getColumns()) { 489 | if (c.getDefinition()["visible"] === false) { 490 | let title = c.getDefinition().title; 491 | hidableColumns[title] = c; 492 | e("#shown-columns").append(newE("option", { value: title }, "+ " + title)); 493 | } 494 | } 495 | 496 | e("#shown-columns").addEventListener("change", () => { 497 | let selected = e("#shown-columns").selectedOptions[0].value; 498 | if (selected == "few") { 499 | showColumnsSelector(false); 500 | // Hide hidable columns 501 | setTimeout(() => { 502 | for (let c in hidableColumns) { 503 | hidableColumns[c].hide(); 504 | } 505 | table.redraw(); 506 | showColumnsSelector(true); 507 | }, 10); 508 | } else if (selected == "all") { 509 | // Show all columns 510 | showColumnsSelector(false); 511 | setTimeout(() => { 512 | for (let c of table.getColumns()) { 513 | c.show(); 514 | } 515 | table.redraw(); 516 | showColumnsSelector(true); 517 | }, 10); 518 | } else { 519 | // Show selected columns 520 | hidableColumns[selected].show(); 521 | // the new column may change row height 522 | table.redraw(); 523 | } 524 | }); 525 | 526 | }); 527 | } 528 | }); 529 | -------------------------------------------------------------------------------- /js/vendors.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | GENERATED FILE 4 | from http://c0ezh785.caspio.com/ 5 | */ 6 | 7 | // eslint-disable-next-line no-unused-vars 8 | const vendors = { 9 | 10 | // extracted 11 | "0010": "Sharp", 12 | "0011": "FUJITSU CONNECTED TECHNOLOGIES LIMITED", 13 | "0012": "RaonSecure Co., Ltd.", 14 | "0013": "ETRI", 15 | "0014": "CrucialTec", 16 | "0015": "Egis Technology Inc.", 17 | "0016": "Sensory, Inc.", 18 | "0019": "GOTRUSTID Inc.", 19 | "001B": "Huawei Device Co., Ltd.", 20 | "001D": "Shenzhen National Engineering Laboratory of Digital Television Co.,Ltd.", 21 | "001F": "EyeVerify, Inc.", 22 | "0020": "Dream Security Co., Ltd. Korea", 23 | "0024": "Giesecke & Devrient", 24 | "0027": "Secuve Co., Ltd.", 25 | "0028": "SGA Solutions", 26 | "002A": "Coolpad Group Limited", 27 | "002C": "KT", 28 | "002E": "Hancom WITH", 29 | "0032": "Open Security Research", 30 | "0033": "Tobesmart", 31 | "0037": "Queralt Inc.", 32 | "0038": "Redrock Biometrics, Inc.", 33 | "0039": "Lightfactor", 34 | "003A": "LG Uplus Corp.", 35 | "003B": "", 36 | "003D": "IsItYou, Ltd.", 37 | "003F": "thinkAT", 38 | "0040": "Highmaru Inc.", 39 | "0041": "Gallagher North America Inc.", 40 | "0042": "SsenStone", 41 | "0043": "KICA Inc.", 42 | "0044": "NBREDS Inc", 43 | "0045": "HYPR", 44 | "0048": "IRISYS CO.,Ltd.", 45 | "0049": "HYUNDAI MOTOR GROUP", 46 | "004A": "Uni-ID Technology (Beijing) Co.,Ltd", 47 | "004B": "Ji Nan Sheng An Information Technology Co., Ltd", 48 | "004D": "China Financial Certification Authority", 49 | "004F": "Meizu Technology Co., Ltd.", 50 | "0050": "Visionlabs LLC", 51 | "0051": "Dayside, Inc.", 52 | "0052": "i-Sprint Innovations Pte Ltd", 53 | "0054": "IoTrust Co., Ltd", 54 | "0056": "PixelPin Ltd.", 55 | "0057": "Mobile-ID Technologies And Services Joint Stock Company", 56 | "0059": "AIDEEP Co., Ltd.", 57 | "005A": "AirCUVE", 58 | "005B": "Rowem Inc", 59 | "005C": "Penta Security Systems Inc.", 60 | "005D": "Octatco", 61 | "0062": "FUJITSU LIMITED", 62 | "0075": "Tangem AG", 63 | "0076": "Changing Information Technology Inc.", 64 | "0077": "UbiNtisLab Co.,Ltd.", 65 | "0079": "vivo Mobile Communication Co.,Ltd.", 66 | "0080": "CybrSecurity Corporation", 67 | "0081": "Honor Device Co., Ltd.", 68 | "0083": "Hyweb Global Technology Co. Ltd", 69 | "0085": "CyberLink Corp.", 70 | "0087": "Wultra s.r.o.", 71 | "008A": "Tendyron Corporation", 72 | "0090": "CORETECH KNOWLEDGE INC.", 73 | "0091": "Starfish GmbH", 74 | "0093": "Guangdong OPPO Mobile Telecommunications Corp., Ltd.", 75 | "0261": "TWCA", 76 | "0262": "TD Tech Ltd", 77 | "0263": "Presidio Identity", 78 | "096E": "Feitian Technologies Co., Ltd.", 79 | "1111": "SK Planet", 80 | "2E84": "MOTOROLA mobile technology (wuhan) communications co., LTD", 81 | "4359": "Cypress", 82 | "4746": "Shenzhen Goodix Technology Co., Ltd", 83 | "4D48": "Safran Identity & Security", 84 | "4E4E": "Nok Nok Labs", 85 | "5143": "Qualcomm Technologies, Inc.", 86 | "53D5": "Samsung SDS", 87 | "565A": "Verizon", 88 | "5AFE": "Synaptics Incorporated", 89 | "9874": "ING", 90 | "AD10": "Plantronics, Inc.", 91 | "BD51": "OneSpan", 92 | "CD01": "SK Telecom", 93 | "D409": "Daon", 94 | "FACE": "FaceTec", 95 | 96 | // manual 97 | "0017": "LGE", 98 | "001E": "BTWorks", 99 | "0022": "Movenda", 100 | "0030": "Thales", 101 | "0031": "ATsolutions", 102 | "0063": "VTC SmartTech", 103 | "0064": "SecuGen", 104 | "0066": "Capy", 105 | "006F": "Hanko", 106 | "1EA8": "Excelsecu", 107 | "DAB8": "DDS" 108 | }; 109 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fido-mds-explorer", 3 | "version": "1.0.0", 4 | "description": "FIDO MDS Explorer", 5 | "main": "index.html", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/opotonniee/fido-mds-explorer.git" 12 | }, 13 | "author": "", 14 | "license": "Apache-2.0", 15 | "bugs": { 16 | "url": "https://github.com/opotonniee/fido-mds-explorer/issues" 17 | }, 18 | "homepage": "https://github.com/opotonniee/fido-mds-explorer#readme", 19 | "devDependencies": { 20 | "eslint": "^8.57.0" 21 | } 22 | } 23 | --------------------------------------------------------------------------------