Merge branch 'release-1.16' into master

This commit is contained in:
antelle 2020-12-17 19:29:14 +01:00
commit 212042556f
No known key found for this signature in database
GPG Key ID: 63C9777AAB7C563C
11 changed files with 152 additions and 13 deletions

View File

@ -24,6 +24,15 @@ jobs:
run: npm test
- name: Grunt
run: grunt
- name: Write secrets
env:
VIRUS_TOTAL: ${{ secrets.VIRUS_TOTAL }}
run: |
mkdir keys
echo "$VIRUS_TOTAL" > keys/virus-total.json
- name: Check on VirusTotal
run: grunt virustotal
if: ${{ github.repository == 'keeweb/keeweb' }}
- name: Upload artifact
uses: actions/upload-artifact@v1
with:

View File

@ -759,6 +759,16 @@ module.exports = function (grunt) {
headless: true
},
default: 'test/runner.html'
},
virustotal: {
options: {
prefix: `keeweb.v${pkg.version}-${sha}.`,
timeout: 10 * 60 * 1000,
get apiKey() {
return require('./keys/virus-total.json').apiKey;
}
},
html: 'dist/index.html'
}
});
};

View File

@ -5,6 +5,8 @@ import ThemeDefaults from '!!raw-loader!../../styles/themes/_theme-defaults.scss
const ThemeVars = {
themeDefaults: null,
newLineRegEx: /[\n\s]+/g, // don't inline it, see #1656
themeVarsRegEx: /([\w\-]+):([^:]+),(\$)?/g,
init() {
if (this.themeDefaults) {
@ -24,7 +26,7 @@ const ThemeVars = {
apply(cssStyle) {
this.init();
const matches = ThemeVarsScss.replace(/[\n\s]+/g, '').matchAll(/([\w\-]+):([^:]+),(\$)?/g);
const matches = ThemeVarsScss.replace(this.newLineRegEx, '').matchAll(this.themeVarsRegEx);
for (let [, name, def, last] of matches) {
if (last && def.endsWith(')')) {
// definitions are written like this:

View File

@ -397,7 +397,7 @@ class StorageBase {
skipAuth: true,
data: UrlFormat.buildFormData({
'client_id': config.clientId,
'client_secret': config.clientSecret,
...(config.clientSecret ? { 'client_secret': config.clientSecret } : null),
'grant_type': 'authorization_code',
'code': result.code,
'redirect_uri': session.redirectUri,
@ -430,7 +430,7 @@ class StorageBase {
skipAuth: true,
data: UrlFormat.buildFormData({
'client_id': config.clientId,
'client_secret': config.clientSecret,
...(config.clientSecret ? { 'client_secret': config.clientSecret } : null),
'grant_type': 'refresh_token',
'refresh_token': refreshToken
}),
@ -447,9 +447,12 @@ class StorageBase {
if (xhr.status === 400) {
delete this.runtimeData[this.name + 'OAuthToken'];
this._oauthToken = null;
this.logger.error('Error exchanging refresh token, trying to authorize again');
this._oauthAuthorize(callback);
} else {
this.logger.error('Error exchanging refresh token', err);
callback?.('Error exchanging refresh token');
}
this.logger.error('Error exchanging refresh token', err);
callback?.('Error exchanging refresh token');
}
});
}

View File

@ -0,0 +1,97 @@
module.exports = function (grunt) {
grunt.registerMultiTask('virustotal', 'Checks if a file has issues on VirusTotal', function () {
const done = this.async();
const opt = this.options();
const path = require('path');
const fs = require('fs');
const fetch = require('node-fetch');
const FormData = require('form-data');
Promise.all(
this.files[0].src.map((file) =>
checkFile(opt, file).catch((err) => {
grunt.warn('VirusTotal check failed: ' + err);
})
)
).then(done);
async function checkFile(opt, file) {
grunt.log.writeln(`Uploading to VirusTotal: ${file}...`);
const timeStarted = Date.now();
const { apiKey, prefix, timeout = 60 * 1000 } = opt;
const interval = 5000;
const headers = { 'x-apikey': apiKey };
const fileData = fs.readFileSync(file);
const fileName = (prefix || '') + path.basename(file);
const form = new FormData();
form.append('file', fileData, fileName);
const fileUploadResp = await fetch('https://www.virustotal.com/api/v3/files', {
method: 'POST',
headers,
body: form
});
const fileUploadRespData = await fileUploadResp.json();
if (fileUploadRespData.error) {
const errStr = JSON.stringify(fileUploadRespData.error);
throw new Error(`File upload error: ${errStr}`);
}
const id = fileUploadRespData.data.id;
if (!id) {
throw new Error('File upload error: empty id');
}
grunt.log.writeln(`Uploaded ${file} to VirusTotal, id: ${id}`);
let elapsed;
do {
const checkResp = await fetch(`https://www.virustotal.com/api/v3/analyses/${id}`, {
headers
});
const checkRespData = await checkResp.json();
if (checkRespData.error) {
const errStr = JSON.stringify(checkRespData.error);
throw new Error(`File check error: ${errStr}`);
}
const { attributes } = checkRespData.data;
if (attributes.status === 'completed') {
const { stats } = attributes;
if (stats.malicious > 0) {
throw new Error(
`File ${file} reported as malicious ${stats.malicious} time(s)`
);
}
if (stats.suspicious > 0) {
throw new Error(
`File ${file} reported as malicious ${stats.suspicious} time(s)`
);
}
const statsStr = Object.entries(stats)
.map(([k, v]) => `${k}=${v}`)
.join(', ');
grunt.log.writeln(`VirusTotal check OK: ${file}, stats:`, statsStr);
return;
}
elapsed = Date.now() - timeStarted;
grunt.log.writeln(
`VirusTotal check status: ${attributes.status}, elapsed ${elapsed}ms`
);
await wait(interval);
} while (elapsed < timeout);
throw new Error(`Timed out after ${timeout}ms`);
}
function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
});
};

View File

@ -1,6 +1,6 @@
{
"name": "KeeWeb",
"version": "1.16.3",
"version": "1.16.4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "KeeWeb",
"version": "1.16.3",
"version": "1.16.4",
"description": "Free cross-platform password manager compatible with KeePass",
"main": "main.js",
"homepage": "https://keeweb.info",

20
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "keeweb",
"version": "1.16.3",
"version": "1.16.4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -6829,12 +6829,12 @@
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
@ -14294,6 +14294,16 @@
"uuid": "^3.3.2"
},
"dependencies": {
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "keeweb",
"version": "1.16.3",
"version": "1.16.4",
"description": "Free cross-platform password manager compatible with KeePass",
"main": "Gruntfile.js",
"private": true,
@ -45,6 +45,7 @@
"eslint-plugin-promise": "4.2.1",
"eslint-plugin-standard": "4.1.0",
"exports-loader": "1.1.1",
"form-data": "^3.0.0",
"fs-extra": "^9.0.1",
"grunt": "1.3.0",
"grunt-chmod": "^1.1.1",
@ -73,6 +74,7 @@
"mini-css-extract-plugin": "^1.3.1",
"mocha": "^8.2.1",
"morphdom": "^2.6.1",
"node-fetch": "^2.6.1",
"node-sass": "^5.0.0",
"node-stream-zip": "1.12.0",
"normalize.css": "8.0.1",

View File

@ -1,5 +1,9 @@
Release notes
-------------
##### v1.16.4 (2020-12-17)
`-` fix #1656: false positive report on VirusTotal
`+` #1629: possibility to use OneDrive as SPA
##### v1.16.3 (2020-12-10)
`-` fix #1650: keyfiles stored in the app can't be used

2
util/bump-version.js Normal file → Executable file
View File

@ -1,3 +1,5 @@
#!/usr/bin/env node
/* eslint-disable no-console */
const fs = require('fs');