From 2e476bceada66f1d9215208f621a9d39fd9d8903 Mon Sep 17 00:00:00 2001 From: Vlad Oros Date: Tue, 13 Aug 2019 16:53:22 +0300 Subject: [PATCH 1/5] fixing build --- Dockerfile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 95cb345..4303bfb 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:11-alpine -LABEL version="1.2" +LABEL version="1.3" LABEL description="Linux alpine with node:11 and chromium browser" RUN set -x \ @@ -9,9 +9,7 @@ RUN set -x \ && echo @edge http://nl.alpinelinux.org/alpine/edge/community >> /etc/apk/repositories \ && echo @edge http://nl.alpinelinux.org/alpine/edge/main >> /etc/apk/repositories -RUN apk add --no-cache \ - bash \ - python3 +RUN apk add --no-cache bash python3 pkgconfig autoconf automake libtool nasm build-base zlib-dev RUN apk add --no-cache \ chromium@edge \ From 8a82882ee4d038f12640f50cb1bf6af6669bdfcc Mon Sep 17 00:00:00 2001 From: Andrei Domsa Date: Wed, 25 Sep 2019 12:52:05 +0000 Subject: [PATCH 2/5] Merged in feature/MDC-398 (pull request #8) Feature/MDC-398 * MDC-398 update icons font, add new icons * MDC-398 add metadefender core client * MDC-398 add core settings * implement file processing with core client * MDC-398 add hash lookup to core + refactor file processor * fix core settings update * add core settings validation * add core settings saved notification * add notifications for errors * fix rule selection and core progress url * fix save core settings for first configuration * update core settings validation * update about page - remove settings help * add secondary text to cre settings checkbox * core fixes - fixed apikey validation - fixed sanitized files detection - handle unauthorized santized urls * feature/MDC-398 url uppercase * Update error messages * MDC-603 - move upload file size limit - add sandbox limit to apikey info - add link to github repo * feature/MDC-398 Url -> URL * feature/MDC-398 adding url for core guide * add ga exception tracking * handle xhr errors * remove deep cdr workflow from settings * enable validate and save only when the form data is changed * feature/MDC-398 fixing license url Approved-by: Vlad Oros --- app/_locales/en/messages.json | 55 +- app/fonts/fontello/LICENSE.txt | 18 - app/fonts/fontello/config.json | 60 ++- .../fontello/css/mcl-ext-icons-codes.css | 14 +- .../fontello/css/mcl-ext-icons-embedded.css | 26 +- .../fontello/css/mcl-ext-icons-ie7-codes.css | 14 +- app/fonts/fontello/css/mcl-ext-icons-ie7.css | 14 +- app/fonts/fontello/css/mcl-ext-icons.css | 28 +- app/fonts/fontello/demo.html | 26 +- app/fonts/fontello/font/mcl-ext-icons.eot | Bin 6916 -> 7568 bytes app/fonts/fontello/font/mcl-ext-icons.svg | 18 +- app/fonts/fontello/font/mcl-ext-icons.ttf | Bin 6728 -> 7380 bytes app/fonts/fontello/font/mcl-ext-icons.woff | Bin 4148 -> 4524 bytes app/fonts/fontello/font/mcl-ext-icons.woff2 | Bin 3404 -> 3772 bytes app/html/extension/about.html | 39 +- app/html/extension/history.html | 15 +- app/html/extension/settings.html | 49 ++ app/images/how-to/more-info.png | Bin 10836 -> 0 bytes app/images/how-to/popup.png | Bin 8044 -> 0 bytes app/images/how-to/save-clean.png | Bin 6389 -> 0 bytes app/images/how-to/scan-all.png | Bin 11210 -> 0 bytes app/scripts/background/background-task.js | 46 +- .../common/browser/browser-notification.js | 6 +- app/scripts/common/core-client.js | 257 ++++++++++ app/scripts/common/file-processor.js | 472 ++++++++++-------- app/scripts/common/persistent/apikey-info.js | 8 +- app/scripts/common/persistent/settings.js | 46 +- app/scripts/common/scan-file.js | 12 + app/scripts/extension/settings.controller.js | 155 +++++- app/styles/extension.scss | 39 +- 30 files changed, 999 insertions(+), 418 deletions(-) delete mode 100644 app/images/how-to/more-info.png delete mode 100644 app/images/how-to/popup.png delete mode 100644 app/images/how-to/save-clean.png delete mode 100644 app/images/how-to/scan-all.png create mode 100755 app/scripts/common/core-client.js diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 299bc37..60d2d9d 100755 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -48,7 +48,7 @@ "description": "" }, "aboutThankYou": { - "message": "Thank you for installing OPSWAT File Security for Chrome! This extension gives you the ability to scan downloads* for malware with 30+ anti-malware engines using OPSWAT's MetaDefender Cloud.
* maximum file size is 140 MB", + "message": "Thank you for installing OPSWAT File Security for Chrome! This extension gives you the ability to scan downloads* for malware with 30+ anti-malware engines using OPSWAT's MetaDefender Cloud.", "description": "" }, "aboutApiKeyInfo": { @@ -60,7 +60,7 @@ "description": "" }, "aboutMoreAboutMCL": { - "message": "Read more about MetaDefender Cloud Licensing options", + "message": "Read more about MetaDefender Cloud Licensing options", "description": "" }, "aboutHowToUse": { @@ -79,17 +79,11 @@ "message": "To access the OPSWAT File Security for Chrome extension settings page, right click on the 'OPSWAT File Security for Chrome' button in the Chrome top bar and select 'Options' from the drop down menu", "description": "" }, - "howToScanAll": { - "message": "If you would like to automatically scan all downloads, click on the 'OPSWAT File Security for Chrome button' in the Chrome top bar and select the box to 'Scan downloads'", - "description": "" - }, - "howToSaveClean": { - "message": "If you would like to automatically download the scanned files with no threat detected, go to OPSWAT File Security for Chrome settings page and enable 'Save clean files'", - "description": "" + "chromeExtensionHelp": { + "message": "Read more about OPSWAT File Security for Chrome extension settings on our online help page." }, - "howToMoreInfo": { - "message": "To view more information about the scan results, go to scan history page and click on the file result", - "description": "" + "githubInfo": { + "message": "The chrome extension is an open-source tool developed by OPSWAT, the source code is published on github: metadefender-browser-extension" }, "contactOpswat": { "message": "Contact OPSWAT Sales to increase your limits", @@ -129,6 +123,22 @@ "safeUrlSub": { "message": "Read more about Safe URL Redirect" }, + "useCore": { + "message": "On-premise scanning using MetaDefender Core
Send your files to a private MetaDefender Core server
", + "description": "" + }, + "useCoreSub": { + "message": "" + }, + "coreUrl": { + "message": "URL" + }, + "coreApikey": { + "message": "Apikey" + }, + "coreRule": { + "message": "Workflow" + }, "searcHistory": { "message": "Search history" }, @@ -140,6 +150,22 @@ "message": "Threats detected", "description": "" }, + "coreSettingsSave": { + "message": "Validate & Save", + "description": "Save core settings button label" + }, + "coreSettingsSavedNotification": { + "message": "Core settings saved!" + }, + "coreSettingsInvalidNotification": { + "message": "Core settings are not valid!" + }, + "coreSettingsInvalidApikey": { + "message": "Invalid MetaDefender Core apikey!" + }, + "coreSettingsInvalidUrl": { + "message": "Could not connect to a MetaDefender Core server at the specified URL!" + }, "scanStatusScanning": { "message": "Scan in progress...", "description": "" @@ -157,7 +183,7 @@ "description": "" }, "unsupportedUrl": { - "message": "Url not supported.\nYou may be able to scan the file by downloading it while 'Scan Downloads' is enabled" + "message": "URL not supported.\nYou may be able to scan the file by downloading it while 'Scan Downloads' is enabled" }, "errorWhileDownloading": { "message": "We encountered an error while downloading this file. Please try again", @@ -167,6 +193,9 @@ "message": "Unable to access local file. Please make sure 'Allow access to file URLs' is enabled for OPSWAT extension", "description": "" }, + "scanFileError": { + "message": "There was an error while scanning the file. Please try again later" + }, "fileAccessDisabled": { "message": "To use this feature, 'Allow access to file URLs' must be enabled for OPSWAT extension" }, diff --git a/app/fonts/fontello/LICENSE.txt b/app/fonts/fontello/LICENSE.txt index aa4b284..d244c95 100644 --- a/app/fonts/fontello/LICENSE.txt +++ b/app/fonts/fontello/LICENSE.txt @@ -1,24 +1,6 @@ Font license info -## Iconic - - Copyright (C) 2012 by P.J. Onori - - Author: P.J. Onori - License: SIL (http://scripts.sil.org/OFL) - Homepage: http://somerandomdude.com/work/iconic/ - - -## MFG Labs - - Copyright (C) 2012 by Daniel Bruce - - Author: MFG Labs - License: SIL (http://scripts.sil.org/OFL) - Homepage: http://www.mfglabs.com/ - - ## Font Awesome Copyright (C) 2016 by Dave Gandy diff --git a/app/fonts/fontello/config.json b/app/fonts/fontello/config.json index 820a3b0..4058474 100644 --- a/app/fonts/fontello/config.json +++ b/app/fonts/fontello/config.json @@ -6,12 +6,6 @@ "units_per_em": 1000, "ascent": 850, "glyphs": [ - { - "uid": "f48ae54adfb27d8ada53d0fd9e34ee10", - "css": "trash-empty", - "code": 59394, - "src": "fontawesome" - }, { "uid": "9bc2902722abb366a213a052ade360bc", "css": "spin", @@ -25,34 +19,52 @@ "src": "fontawesome" }, { - "uid": "1d35198f5190ec004dd4ec742fbe19ca", - "css": "right", - "code": 59393, - "src": "mfglabs" + "uid": "2c413e78faf1d6631fd7b094d14c2253", + "css": "cloud", + "code": 59395, + "src": "fontawesome" }, { - "uid": "ec21fe3492bb04d9e29103c319556ed8", - "css": "search", - "code": 62733, - "src": "mfglabs" + "uid": "bbfb51903f40597f0b70fd75bc7b5cac", + "css": "trash", + "code": 61944, + "src": "fontawesome" }, { - "uid": "ce50292e85eb5d6ee3be61b32bf2bdf3", - "css": "ok", - "code": 59399, - "src": "mfglabs" + "uid": "e99461abfef3923546da8d745372c995", + "css": "cog", + "code": 59394, + "src": "fontawesome" }, { - "uid": "06301c50d89b5d3e651bd07ebd6d7de7", + "uid": "9dd9e835aebe1060ba7190ad2b2ed951", + "css": "search", + "code": 59392, + "src": "fontawesome" + }, + { + "uid": "5211af474d3a9848f67f945e2ccaf143", "css": "cancel", + "code": 59396, + "src": "fontawesome" + }, + { + "uid": "12f4ece88e46abd864e40b35e05b11cd", + "css": "ok", "code": 59400, - "src": "mfglabs" + "src": "fontawesome" }, { - "uid": "fc94b92194752796654c96c7b7dccebb", - "css": "cog", - "code": 59392, - "src": "iconic" + "uid": "ad6b3fbb5324abe71a9c0b6609cbb9f1", + "css": "right", + "code": 59397, + "src": "fontawesome" + }, + { + "uid": "d59ff824282fc6edaeca991deab522aa", + "css": "server", + "code": 62003, + "src": "fontawesome" } ] } \ No newline at end of file diff --git a/app/fonts/fontello/css/mcl-ext-icons-codes.css b/app/fonts/fontello/css/mcl-ext-icons-codes.css index f0f794b..a5bd335 100644 --- a/app/fonts/fontello/css/mcl-ext-icons-codes.css +++ b/app/fonts/fontello/css/mcl-ext-icons-codes.css @@ -1,9 +1,11 @@ -.icon-cog:before { content: '\e800'; } /* '' */ -.icon-right:before { content: '\e801'; } /* '' */ -.icon-trash-empty:before { content: '\e802'; } /* '' */ -.icon-ok:before { content: '\e807'; } /* '' */ -.icon-cancel:before { content: '\e808'; } /* '' */ +.icon-search:before { content: '\e800'; } /* '' */ +.icon-cog:before { content: '\e802'; } /* '' */ +.icon-cloud:before { content: '\e803'; } /* '' */ +.icon-cancel:before { content: '\e804'; } /* '' */ +.icon-right:before { content: '\e805'; } /* '' */ +.icon-ok:before { content: '\e808'; } /* '' */ .icon-spin:before { content: '\e839'; } /* '' */ .icon-help:before { content: '\f128'; } /* '' */ -.icon-search:before { content: '\f50d'; } /* '' */ \ No newline at end of file +.icon-trash:before { content: '\f1f8'; } /* '' */ +.icon-server:before { content: '\f233'; } /* '' */ \ No newline at end of file diff --git a/app/fonts/fontello/css/mcl-ext-icons-embedded.css b/app/fonts/fontello/css/mcl-ext-icons-embedded.css index 2d94694..f52f328 100644 --- a/app/fonts/fontello/css/mcl-ext-icons-embedded.css +++ b/app/fonts/fontello/css/mcl-ext-icons-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'mcl-ext-icons'; - src: url('../font/mcl-ext-icons.eot?84545260'); - src: url('../font/mcl-ext-icons.eot?84545260#iefix') format('embedded-opentype'), - url('../font/mcl-ext-icons.svg?84545260#mcl-ext-icons') format('svg'); + src: url('../font/mcl-ext-icons.eot?66982893'); + src: url('../font/mcl-ext-icons.eot?66982893#iefix') format('embedded-opentype'), + url('../font/mcl-ext-icons.svg?66982893#mcl-ext-icons') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'mcl-ext-icons'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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YFlmsAFjYCMgsAErI7AEQ2CwASuwBSVhsAUlsAJiILAAUFiwQGBZZrABY7AEJmEgsAQlYGQjsAMlYGRQWCEbIyFZIyAgsAQmI0ZhOFktsDcssAAWICAgsAUmIC5HI0cjYSM8OC2wOCywABYgsAgjQiAgIEYjR7ABKyNhOC2wOSywABawAyWwAiVHI0cjYbAAVFguIDwjIRuwAiWwAiVHI0cjYSCwBSWwBCVHI0cjYbAGJbAFJUmwAiVhuQgACABjYyMgWGIbIVljuAQAYiCwAFBYsEBgWWawAWNgIy4jICA8ijgjIVktsDossAAWILAIQyAuRyNHI2EgYLAgYGawAmIgsABQWLBAYFlmsAFjIyAgPIo4LbA7LCMgLkawAiVGUlggPFkusSsBFCstsDwsIyAuRrACJUZQWCA8WS6xKwEUKy2wPSwjIC5GsAIlRlJYIDxZIyAuRrACJUZQWCA8WS6xKwEUKy2wPiywNSsjIC5GsAIlRlJYIDxZLrErARQrLbA/LLA2K4ogIDywBCNCijgjIC5GsAIlRlJYIDxZLrErARQrsARDLrArKy2wQCywABawBCWwBCYgLkcjRyNhsAlDKyMgPCAuIzixKwEUKy2wQSyxCAQlQrAAFrAEJbAEJSAuRyNHI2EgsAQjQrAJQysgsGBQWCCwQFFYswIgAyAbswImAxpZQkIjIEewBEOwAmIgsABQWLBAYFlmsAFjYCCwASsgiophILACQ2BkI7ADQ2FkUFiwAkNhG7ADQ2BZsAMlsAJiILAAUFiwQGBZZrABY2GwAiVGYTgjIDwjOBshICBGI0ewASsjYTghWbErARQrLbBCLLA1Ky6xKwEUKy2wQyywNishIyAgPLAEI0IjOLErARQrsARDLrArKy2wRCywABUgR7AAI0KyAAEBFRQTLrAxKi2wRSywABUgR7AAI0KyAAEBFRQTLrAxKi2wRiyxAAEUE7AyKi2wRyywNCotsEgssAAWRSMgLiBGiiNhOLErARQrLbBJLLAII0KwSCstsEossgAAQSstsEsssgABQSstsEwssgEAQSstsE0ssgEBQSstsE4ssgAAQistsE8ssgABQistsFAssgEAQistsFEssgEBQistsFIssgAAPistsFMssgABPistsFQssgEAPistsFUssgEBPistsFYssgAAQCstsFcssgABQCstsFgssgEAQCstsFkssgEBQCstsFossgAAQystsFsssgABQystsFwssgEAQystsF0ssgEBQystsF4ssgAAPystsF8ssgABPystsGAssgEAPystsGEssgEBPystsGIssDcrLrErARQrLbBjLLA3K7A7Ky2wZCywNyuwPCstsGUssAAWsDcrsD0rLbBmLLA4Ky6xKwEUKy2wZyywOCuwOystsGgssDgrsDwrLbBpLLA4K7A9Ky2waiywOSsusSsBFCstsGsssDkrsDsrLbBsLLA5K7A8Ky2wbSywOSuwPSstsG4ssDorLrErARQrLbBvLLA6K7A7Ky2wcCywOiuwPCstsHEssDorsD0rLbByLLMJBAIDRVghGyMhWUIrsAhlsAMkUHiwARUwLQBLuADIUlixAQGOWbABuQgACABjcLEABUKyAAEAKrEABUKzCgIBCCqxAAVCsw4AAQgqsQAGQroCwAABAAkqsQAHQroAQAABAAkqsQMARLEkAYhRWLBAiFixA2REsSYBiFFYugiAAAEEQIhjVFixAwBEWVlZWbMMAgEMKrgB/4WwBI2xAgBEAAA=') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'mcl-ext-icons'; - src: url('../font/mcl-ext-icons.svg?84545260#mcl-ext-icons') format('svg'); + src: url('../font/mcl-ext-icons.svg?66982893#mcl-ext-icons') format('svg'); } } */ @@ -52,11 +52,13 @@ /* Uncomment for 3D effect */ /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ } -.icon-cog:before { content: '\e800'; } /* '' */ -.icon-right:before { content: '\e801'; } /* '' */ -.icon-trash-empty:before { content: '\e802'; } /* '' */ -.icon-ok:before { content: '\e807'; } /* '' */ -.icon-cancel:before { content: '\e808'; } /* '' */ +.icon-search:before { content: '\e800'; } /* '' */ +.icon-cog:before { content: '\e802'; } /* '' */ +.icon-cloud:before { content: '\e803'; } /* '' */ +.icon-cancel:before { content: '\e804'; } /* '' */ +.icon-right:before { content: '\e805'; } /* '' */ +.icon-ok:before { content: '\e808'; } /* '' */ .icon-spin:before { content: '\e839'; } /* '' */ .icon-help:before { content: '\f128'; } /* '' */ -.icon-search:before { content: '\f50d'; } /* '' */ \ No newline at end of file +.icon-trash:before { content: '\f1f8'; } /* '' */ +.icon-server:before { content: '\f233'; } /* '' */ \ No newline at end of file diff --git a/app/fonts/fontello/css/mcl-ext-icons-ie7-codes.css b/app/fonts/fontello/css/mcl-ext-icons-ie7-codes.css index 88e07f6..68c5d33 100644 --- a/app/fonts/fontello/css/mcl-ext-icons-ie7-codes.css +++ b/app/fonts/fontello/css/mcl-ext-icons-ie7-codes.css @@ -1,9 +1,11 @@ -.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-right { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-trash-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-ok { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-search { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cloud { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-right { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-ok { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-help { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-search { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file +.icon-trash { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-server { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/app/fonts/fontello/css/mcl-ext-icons-ie7.css b/app/fonts/fontello/css/mcl-ext-icons-ie7.css index 8832747..068576f 100644 --- a/app/fonts/fontello/css/mcl-ext-icons-ie7.css +++ b/app/fonts/fontello/css/mcl-ext-icons-ie7.css @@ -10,11 +10,13 @@ /* font-size: 120%; */ } -.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-right { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-trash-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-ok { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-search { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cloud { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-right { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-ok { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-help { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-search { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file +.icon-trash { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-server { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/app/fonts/fontello/css/mcl-ext-icons.css b/app/fonts/fontello/css/mcl-ext-icons.css index 9e1995f..22e025b 100644 --- a/app/fonts/fontello/css/mcl-ext-icons.css +++ b/app/fonts/fontello/css/mcl-ext-icons.css @@ -1,11 +1,11 @@ @font-face { font-family: 'mcl-ext-icons'; - src: url('../font/mcl-ext-icons.eot?20163023'); - src: url('../font/mcl-ext-icons.eot?20163023#iefix') format('embedded-opentype'), - url('../font/mcl-ext-icons.woff2?20163023') format('woff2'), - url('../font/mcl-ext-icons.woff?20163023') format('woff'), - url('../font/mcl-ext-icons.ttf?20163023') format('truetype'), - url('../font/mcl-ext-icons.svg?20163023#mcl-ext-icons') format('svg'); + src: url('../font/mcl-ext-icons.eot?98126950'); + src: url('../font/mcl-ext-icons.eot?98126950#iefix') format('embedded-opentype'), + url('../font/mcl-ext-icons.woff2?98126950') format('woff2'), + url('../font/mcl-ext-icons.woff?98126950') format('woff'), + url('../font/mcl-ext-icons.ttf?98126950') format('truetype'), + url('../font/mcl-ext-icons.svg?98126950#mcl-ext-icons') format('svg'); font-weight: normal; font-style: normal; } @@ -15,7 +15,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'mcl-ext-icons'; - src: url('../font/mcl-ext-icons.svg?20163023#mcl-ext-icons') format('svg'); + src: url('../font/mcl-ext-icons.svg?98126950#mcl-ext-icons') format('svg'); } } */ @@ -55,11 +55,13 @@ /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ } -.icon-cog:before { content: '\e800'; } /* '' */ -.icon-right:before { content: '\e801'; } /* '' */ -.icon-trash-empty:before { content: '\e802'; } /* '' */ -.icon-ok:before { content: '\e807'; } /* '' */ -.icon-cancel:before { content: '\e808'; } /* '' */ +.icon-search:before { content: '\e800'; } /* '' */ +.icon-cog:before { content: '\e802'; } /* '' */ +.icon-cloud:before { content: '\e803'; } /* '' */ +.icon-cancel:before { content: '\e804'; } /* '' */ +.icon-right:before { content: '\e805'; } /* '' */ +.icon-ok:before { content: '\e808'; } /* '' */ .icon-spin:before { content: '\e839'; } /* '' */ .icon-help:before { content: '\f128'; } /* '' */ -.icon-search:before { content: '\f50d'; } /* '' */ \ No newline at end of file +.icon-trash:before { content: '\f1f8'; } /* '' */ +.icon-server:before { content: '\f233'; } /* '' */ \ No newline at end of file diff --git a/app/fonts/fontello/demo.html b/app/fonts/fontello/demo.html index 0ec916f..8220bdc 100644 --- a/app/fonts/fontello/demo.html +++ b/app/fonts/fontello/demo.html @@ -229,11 +229,11 @@ } @font-face { font-family: 'mcl-ext-icons'; - src: url('./font/mcl-ext-icons.eot?50590932'); - src: url('./font/mcl-ext-icons.eot?50590932#iefix') format('embedded-opentype'), - url('./font/mcl-ext-icons.woff?50590932') format('woff'), - url('./font/mcl-ext-icons.ttf?50590932') format('truetype'), - url('./font/mcl-ext-icons.svg?50590932#mcl-ext-icons') format('svg'); + src: url('./font/mcl-ext-icons.eot?16275132'); + src: url('./font/mcl-ext-icons.eot?16275132#iefix') format('embedded-opentype'), + url('./font/mcl-ext-icons.woff?16275132') format('woff'), + url('./font/mcl-ext-icons.ttf?16275132') format('truetype'), + url('./font/mcl-ext-icons.svg?16275132#mcl-ext-icons') format('svg'); font-weight: normal; font-style: normal; } @@ -298,16 +298,20 @@

mcl-ext-icons font demo

-
icon-cog0xe800
-
icon-right0xe801
-
icon-trash-empty0xe802
-
icon-ok0xe807
+
icon-search0xe800
+
icon-cog0xe802
+
icon-cloud0xe803
+
icon-cancel0xe804
-
icon-cancel0xe808
+
icon-right0xe805
+
icon-ok0xe808
icon-spin0xe839
icon-help0xf128
-
icon-search0xf50d
+
+
+
icon-trash0xf1f8
+
icon-server0xf233
diff --git a/app/fonts/fontello/font/mcl-ext-icons.eot b/app/fonts/fontello/font/mcl-ext-icons.eot index dd345678562a300578f0456c114d48ec75adb4ee..e02356a1256effa62d555613db8de234549d870c 100644 GIT binary patch delta 2434 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 -Copyright (C) 2018 by original authors @ fontello.com +Copyright (C) 2019 by original authors @ fontello.com - + - + - + - + - + + + - + + + \ No newline at end of file diff --git a/app/fonts/fontello/font/mcl-ext-icons.ttf b/app/fonts/fontello/font/mcl-ext-icons.ttf index 8ff513ae18bab0918ec93f42b5159d64145b8748..7ebf2f2bb6c44cbde5d0356e9086cdb81d2f46f2 100644 GIT binary patch delta 2429 zcmZ`*Z%kX)6+h?R_t!Rl&+z;lz!+n|CN{MbV_^JQ>QYhCl{A#Plr&WeGX{qMwt&zq ziR5aNrnZ`*6elQ}G-*v>%7<#wntiOqmwl-EW!;n|n$%B~Dp}M1G?6N$Q)2JDXCf_= z_MXqZ_ndp~J->U-x##)w)ZL-0q0;MP05}BzGuhSo;-8b3c8R|YzyjH=&7ky`ngF1G zM)SeN;?nAeA6-2~{8s?Z;?n#^k>oo4eowu!wDOb18}Yv<0r=;{ev;44FPIm-Ujw){ zX?`G20{J|@N&F|ocjZ?%FNEKGul7$8d`W`dl|pu2z3DQEe@y(L)%goWoN;*p8Vurt zYxAqQuixIkLwt&isKvs@=BH!#g9IQ=^BcwWT=AP1Vg%Md`O%}(_+B}K$JoQZ@->W< zb^u8S;3{x?)JwgCj=BQ^ERbBMmjhIwB36{j(1V@_j~@2bYPN(uE3x-^+MvDCgr_fU z35<3UKm-Y7P(ZcpFqFzi*}uzarj}gx+V>B3pvejiEwX@BSe6m^qGe2#)x$Yncf(N!u^qO zM3zF-hEs#ba4?*}I7a$Y1JC2Y;DN9&*-i|-BbYSX(ew=tBz*^)$s`CM8=1Z^&1N=d z(67jOhqI|i61Ay@k}RKSZc$}HzpS|QR^LfUmtHajQR#7L%A%~GL(D5~U$?C+D<=-M zs0#O7mR+d%eJ4dtK5httT32@9nUPFpE0eik&FcYgYf^Gc-YFDE8t_UpOam(G4)@%&Jj_3(SM-ZqNA0nNICMoaD>9p3hilsbEAV( zI!^#H@-qWQZimIr!$~gsV%>%$cZ9-&DRL9Jt1oH#tj24|0TdFK-+J=#(zOy!rSbOG z?3GYtU?k%^e&&y-F5`CoM;8OSvUU5%Q=OTSXs3rt2Hl3*zt~;&bLfIVx;50S%YtQ@ zvXiV*qQau#Hw-~MM0P2Z6hR7F0>i22kpf4tq7YG3C^$4tS$S8R)O+>P?k=^Hx=uZH zy;e`WrtR)(MbjGW?P<2=Gm2W=Qm{e2&->XwAOKzIj(Vbzv`EO1Whm!x)^g3H*&Vi_ zp&{GQEI{O%A7Uvr;OjWR=R=eC8`bOPus3R&x2o^9b#=Aj2e(W!>K!)moPomuqk7FS zqTW&Ot!Q8(5XC)Xlvr=|x?zE#;;_eVfEU`*EgAw+U>H(NXlMt<@EKNM>L?9C72VbqNplntRB@_P*I&cHT%Z& z$rBSVy_gP%n!?u8=@#2!-JVpKuB7Dk7$)7cfk98;e+AF-<%t0UOtX>4lpcs#m6z8UQ3 zw_hr1^_qsn660n89N<)-?Lyb&?dZn?nqZ?({$LS57l;Q~C>nZPw%O8Co*l?jj}P%m zozlneO;q>IV6ZnBWN~}Q{73vk7asLldjx~k@q70$VA1{i_Pw<-^Sr}3_Kr6C9XPrD zar^79&PJ?!wffq(wPh+P9H}R`HFfaVTk&7lYMlOt zkXDU*=mOB#GQlggZ`la-mdrLut6oJNCvgYAVg|d&CH|=}WAEyOb5_XdVbB9VkYIy` zzbXIL@f{A8cb#A0(4B&79ba?OUFImDNJ%ZDf+{-DNuiTAa`WriJkJ)E#OzAp{DPF7 rU(4oJ#PzdF`At?hCu|hYt_k_vN>SWgpWn#Sg7vN3I@}4nL$3b-5{|#u delta 1748 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 delta 2413 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 literal 3404 zcmV-S4YTrhPew8T0RR9101Zq44*&oF02)XD01Wm30RR9100000000000000000000 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 diff --git a/app/html/extension/about.html b/app/html/extension/about.html index 3688305..80b1089 100755 --- a/app/html/extension/about.html +++ b/app/html/extension/about.html @@ -4,6 +4,10 @@

+

+ +

+

@@ -25,18 +29,22 @@

{{vm.apikeyInfo.feedLimit}} +
+ + {{vm.apikeyInfo.sandboxLimit}} +
{{vm.__MSG.getMessage((vm.apikeyInfo.paidUser) ? 'yes' : 'no')}}
- - {{vm.apikeyInfo.limitInterval}} + + {{vm.apikeyInfo.maxUploadFileSize}} MB
{{vm.__MSG.getMessage('contactOpswat')}} -

+

@@ -55,29 +63,4 @@

- -
-
- -
- OPSWAT File Security for Chrome scan downloads -
-
- -
-
- -
- OPSWAT File Security for Chrome save clean files -
-
- -
-
- -
- OPSWAT File Security for Chrome save clean files -
-
- \ No newline at end of file diff --git a/app/html/extension/history.html b/app/html/extension/history.html index 6484c04..12901ab 100755 --- a/app/html/extension/history.html +++ b/app/html/extension/history.html @@ -31,11 +31,14 @@ - - {{file.fileName | decodeFileNameFilter}} - -
{{file.sha256}}
- + + {{vm.momentFrom(file.scanTime)}} @@ -43,7 +46,7 @@ {{file.statusLabel}} - + diff --git a/app/html/extension/settings.html b/app/html/extension/settings.html index 8fe5635..baac468 100755 --- a/app/html/extension/settings.html +++ b/app/html/extension/settings.html @@ -1,24 +1,73 @@
    +
  • {{vm.__MSG.getMessage('fileAccessDisabled')}} {{vm.__MSG.getMessage('goToExtension')}}
    {{vm.__MSG.getMessage('scanDownloadsSub')}}
  • +
  • +
  • +
  • +
  • + +
  • + +
    +
    + +
    + +
    + + + {{vm.__MSG.getMessage('coreSettingsInvalidApikey')}} +
    +
    + +
    + +
    + + + {{vm.__MSG.getMessage('coreSettingsInvalidUrl')}} +
    +
    + +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    +
\ No newline at end of file diff --git a/app/images/how-to/more-info.png b/app/images/how-to/more-info.png deleted file mode 100644 index 70e0bf0441c4cf74db8453541cdebd504a464321..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10836 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 VxA2vC2>o3K9Zf@xl1ER2{|6V6i)#P? diff --git a/app/images/how-to/popup.png b/app/images/how-to/popup.png deleted file mode 100644 index 38d2d34c58090bc8109e1a2f376b37c074365b0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8044 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 Hb?AQpWi6C9 diff --git a/app/images/how-to/save-clean.png b/app/images/how-to/save-clean.png deleted file mode 100644 index 6f6f47bc89ec807b9395e172907ac44627f90ac2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6389 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 R*o3+~SXoo4^wHBd{|0AM@*MyG diff --git a/app/images/how-to/scan-all.png b/app/images/how-to/scan-all.png deleted file mode 100644 index 69f3793deede4f6b3648f42319e5e6bcc0571f64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11210 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%0DHh0w= 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 d-6u{ir~IjQ9aL;9a4oiYYD!v)g- { const cookie = info.cookie; @@ -55,22 +45,33 @@ class BackgroundTask { chrome.notifications.onClosed.addListener(() => { }); browserMessage.addListener(this.messageListener.bind(this)); - } async init() { const settings = this.settings; const apiKeyInfo = this.apikeyInfo; const scanHistory = this.scanHistory; - const fileProcessor = this.fileProcessor; await settings.init(); await apiKeyInfo.init(); await scanHistory.init(); await scanHistory.cleanPendingFiles(); - await fileProcessor.init(); + + MetascanClient.configure({ + pollingIncrementor: MCL_CONFIG.scanResults.incrementor, + pollingMaxInterval: MCL_CONFIG.scanResults.maxInterval + }) + .setHost(MCL_CONFIG.metadefenderDomain) + .setVersion(MCL_CONFIG.metadefenderVersion); + + CoreClient.configure({ + apikey: settings.coreApikey, + endpoint: settings.coreUrl, + pollingIncrementor: MCL_CONFIG.scanResults.incrementor, + pollingMaxInterval: MCL_CONFIG.scanResults.maxInterval, + }); - this.downloadsManager = new DownloadsManager(fileProcessor); + this.downloadsManager = new DownloadManager(FileProcessor); const downloadsManager = this.downloadsManager; chrome.downloads.onCreated.addListener(downloadsManager.trackInProgressDownloads.bind(downloadsManager)); @@ -113,7 +114,8 @@ class BackgroundTask { try { cookieData = JSON.parse(cookieData); } catch (error) { - console.log('setApikey failed', error); + browserNotification.create(error, 'info'); + _gaq.push(['exception', {exDescription: 'background-task:setApikey' + JSON.stringify(error)}]); } if (apikeyInfo.apikey === cookieData.apikey && apikeyInfo.loggedIn === cookieData.loggedIn) { @@ -193,7 +195,10 @@ class BackgroundTask { /** * Extension notifications click event handler */ - async handleNotificationClicks() { + async handleNotificationClicks(notificationId) { + if (notificationId == 'info') { + return; + } goToTab('history'); } @@ -223,6 +228,11 @@ class BackgroundTask { await settings.load(); + CoreClient.configure({ + apikey: settings.coreApikey, + endpoint: settings.coreUrl + }); + if (settings.saveCleanFiles !== saveCleanFiles) { this.updateContextMenu(); } diff --git a/app/scripts/common/browser/browser-notification.js b/app/scripts/common/browser/browser-notification.js index eb36e55..858bf68 100755 --- a/app/scripts/common/browser/browser-notification.js +++ b/app/scripts/common/browser/browser-notification.js @@ -40,7 +40,7 @@ async function create(message, id, fileInfected) { title: chrome.i18n.getMessage('appName'), message: message, priority: 1, - isClickable: true + isClickable: (typeof fileInfected !== 'undefined') }; if (typeof id === 'undefined') { @@ -50,8 +50,8 @@ async function create(message, id, fileInfected) { chrome.notifications.create(String(id), optionObject, clearNotification); } - } catch (e) { - console.error(e); + } catch (error) { + _gaq.push(['exception', {exDescription: 'browser-notification:create' + JSON.stringify(error)}]); } } diff --git a/app/scripts/common/core-client.js b/app/scripts/common/core-client.js new file mode 100755 index 0000000..8845454 --- /dev/null +++ b/app/scripts/common/core-client.js @@ -0,0 +1,257 @@ +'use strict'; + +import 'chromereload/devonly'; + +import browserMessage from './browser/browser-message'; +import { BROWSER_EVENT } from './browser/browser-message-event'; + +import xhr from 'xhr'; + +/** + * + * @type {{configure: configure, setAuth: setAuth, hash: {lookup: hashLookup}, file: {upload: fileUpload, lookup: fileLookup, poolForResults: poolForResults}}} + */ +const CoreClient = { + configure: configure, + setAuth: setAuth, + + // endpoints + file: { + upload: fileUpload, + lookup: fileLookup, + poolForResults: poolForResults, + checkSanitized: checkSanitized + }, + hash: { + lookup: hashLookup + }, + version: getVersion, + rules: getRules +}; + +export default CoreClient; + +const config = { + apikey: null, + endpoint: null, + pollingIncrementor: 1, + pollingMaxInterval: 10000 +}; + +const authHeader = { + 'apikey': null +}; + +/** + * Overwrite default configuration. + * + * @param {*} conf + */ +function configure(conf){ + for (let c in conf) { + if (Object.prototype.hasOwnProperty.call(config, c)) { + config[c] = conf[c]; + } + } + + setAuth(config.apikey); + return this; +} + +/** + * Set client authentication. + * + * @param apikey + * @returns {setAuth} + */ +function setAuth(apikey) { + authHeader['apikey'] = apikey; + return this; +} + +/** + * https://onlinehelp.opswat.com/corev4/8.1.3.1._Process_a_file.html + * + * @param {any} fileData file content + * @param {string} fileName file name + * @param {boolean} canBeSanitized a flag for sanitizable files + * @param {string} rule core scan workflow + * @returns {Promise} + */ +function fileUpload({fileData, fileName, rule}) { + let restEndpoint = `${config.endpoint}/file`; + const httpHeaders = { + 'Content-Type': 'application/octet-stream', + 'user_agent': 'chrome_extension', + 'filename': fileName, + }; + + if (rule) { + httpHeaders.rule = rule; + httpHeaders.workflow = rule; + } + + const options = { + headers: Object.assign({}, authHeader, httpHeaders), + body: fileData + }; + return callAPI(restEndpoint, options, 'post'); +} + +/** + * https://onlinehelp.opswat.com/corev4/8.1.3.2._Fetch_processing_result.html + * + * @param {string} dataId + * @returns {Promise} + */ +function fileLookup(dataId) { + const restEndpoint = `${config.endpoint}/file/${dataId}`; + const options = { + headers: Object.assign({}, authHeader, {}) + }; + + return callAPI(restEndpoint, options); +} + +/** + * https://onlinehelp.opswat.com/corev4/8.1.3.2._Fetch_processing_result.html + * + * @param {string} hash md5|sha1|sha256 + * @returns {Promise} + */ +function hashLookup(hash) { + const restEndpoint = `${config.endpoint}/hash/${hash}`; + const options = { + headers: authHeader + }; + + return callAPI(restEndpoint, options); +} + +/** + * + * @param {string} dataId + * @param {number} pollingInterval + * @returns {Promise} + */ +function poolForResults(dataId, pollingInterval) { + return new Promise((resolve) => { + recursiveLookup(dataId, pollingInterval, resolve); + }); +} + +/** + * + * @param dataId + * @param pollingInterval + * @param resolve + * @returns {Promise.} + */ +async function recursiveLookup(dataId, pollingInterval, resolve) { + let response = await fileLookup(dataId); + + if (response.error) { + return; + } + + if (response.sanitized && Object.prototype.hasOwnProperty.call(response.sanitized, 'file_path')) { + browserMessage.send({ event: BROWSER_EVENT.SANITIZED_FILE_READY, data: { + dataId, + sanitized: response.sanitized + } }); + } + + pollingInterval = Math.min(pollingInterval * config.pollingIncrementor, config.pollingMaxInterval); + + if (response && response.scan_results && response.scan_results.progress_percentage < 100) { + setTimeout(() => { recursiveLookup(dataId, pollingInterval, resolve); }, pollingInterval); + } + else { + resolve(response); + } +} + +/** + * https://onlinehelp.opswat.com/corev4/8.1.8.2._Get_Product_Version.html + * + * @returns {Promise} + */ +function getVersion() { + const restEndpoint = `${config.endpoint}/version`; + const options = { + headers: authHeader + }; + + return callAPI(restEndpoint, options); +} + +/** + * /file/rules + */ +function getRules() { + const restEndpoint = `${config.endpoint}/file/rules`; + const options = { + headers: authHeader + }; + + return callAPI(restEndpoint, options); +} + +/** + * Check if the user has access to the URL. + * + * @param {string} downloadUrl + */ +function checkSanitized(downloadUrl) { + return new Promise((resolve) => { + xhr({ + method: 'get', + url: downloadUrl, + beforeSend: function(xhrObject){ + xhrObject.onprogress = (event) => { + if (event.target.status === 200) { + xhrObject.abort(); + return resolve(true); + } + resolve(false); + }; + } + }, (error, response) => { + if (response.statusCode === 200) { + return resolve(true); + } + resolve(false); + }); + }); +} + +/** + * Call core api and handle http response and errors. + * + * @param {string} endpoint endpoint URL + * @param {*} options http options + * @param {string} verb request type: 'get' | 'post' + * @returns {Promise} + */ +function callAPI(endpoint, options, verb='get') { + return new Promise((resolve, reject) => { + xhr[verb](endpoint, options, (err, resp, body) => { + if (err) { + reject(err); + } + + if (resp.statusCode !== 200) { + return reject({ + statusCode: resp.statusCode, + error: resp.err + }); + } + + try { + resolve(JSON.parse(body)); + } catch (error) { + reject(error); + } + }); + }); +} diff --git a/app/scripts/common/file-processor.js b/app/scripts/common/file-processor.js index 2b2ef99..aeb5178 100644 --- a/app/scripts/common/file-processor.js +++ b/app/scripts/common/file-processor.js @@ -12,266 +12,322 @@ import { apikeyInfo } from '../common/persistent/apikey-info'; import { scanHistory } from '../common/persistent/scan-history'; import BrowserNotification from '../common/browser/browser-notification'; import BrowserMessage from '../common/browser/browser-message'; - -let MetascanClient; +import CoreClient from '../common/core-client'; +import MetascanClient from '../common/metascan-client'; const ON_SCAN_COMPLETE_LISTENERS = []; /** * - * @param metascanClient * @constructor */ -function FileProcessor(metascanClient) { - - MetascanClient = metascanClient; - - this.browserMessage = BrowserMessage; - this.processTarget = processTarget; - this.getDownloadedFile = getDownloadedFile; - this.handleFileScanResults = handleFileScanResults; - this.startStatusPolling = startStatusPolling; - this.init = init; - this.addOnScanCompleteListener = addOnScanCompleteListener; - this.removeOnScanCompleteListener = removeOnScanCompleteListener; - this.callOnScanCompleteListeners = callOnScanCompleteListeners; -} +class FileProcessor { + /** + * Proccess a link to a file or a downloaded file. + * + * @param {string} linkUrl file url + * @param {*} downloadItem https://developer.chrome.com/extensions/downloads#type-DownloadItem + */ + async processTarget(linkUrl, downloadItem) { + + if (!apikeyInfo.apikey) { + BrowserNotification.create(chrome.i18n.getMessage('undefinedApiKey')); + return; + } -async function init() { - await settings.init(); - await apikeyInfo.init(); - await scanHistory.init(); -} + let file = new ScanFile(); -export default FileProcessor; + if (ScanFile.isSanitizedFile(linkUrl)) { + return; + } -async function processTarget(linkUrl, downloadItem) { + if (downloadItem) { + file.fileName = downloadItem.filename.split('/').pop(); + file.size = downloadItem.fileSize; + } + else { + file.fileName = linkUrl.split('/').pop(); + file.fileName = file.fileName.split('?')[0]; + try { + file.size = await ScanFile.getFileSize(linkUrl, file.fileName); + } + catch (errMsg) { + if (errMsg) { + BrowserNotification.create(errMsg); + } + return; + } + } - if (!apikeyInfo.apikey) { - BrowserNotification.create(chrome.i18n.getMessage('undefinedApiKey')); - return; - } + file.fileName = decodeFileName(file.fileName); - let file = new ScanFile(); + file.extension = file.fileName.split('.').pop(); + file.canBeSanitized = file.extension && SANITIZATION_FILE_TYPES.indexOf(file.extension.toLowerCase()) > -1; - if (ScanFile.isSanitizedFile(linkUrl)) { - return; - } + if (file.size === null ) { + BrowserNotification.create(chrome.i18n.getMessage('fileEmpty')); + return; + } - if (downloadItem) { - file.fileName = downloadItem.filename.split('/').pop(); - file.size = downloadItem.fileSize; - } - else { - file.fileName = linkUrl.split('/').pop(); - file.fileName = file.fileName.split('?')[0]; - try { - file.size = await ScanFile.getFileSize(linkUrl, file.fileName); + if (file.size > MCL.config.fileSizeLimit) { + BrowserNotification.create(chrome.i18n.getMessage('fileSizeLimitExceeded')); + return; } - catch (errMsg) { - if (errMsg) { - BrowserNotification.create(errMsg); + + file.statusLabel = ScanFile.getScanStatusLabel(); + + await scanHistory.addFile(file); + + let fileData = null; + + if (downloadItem) { + try { + fileData = await this.getDownloadedFile(downloadItem.localPath || 'file://' + downloadItem.filename); + BrowserNotification.create(chrome.i18n.getMessage('scanStarted') + file.fileName, file.id); + } + catch (e) { + BrowserNotification.create(e, file.id); + scanHistory.removeFile(file); + return; } - return; } - } + else { + if (file.size === 0) { + BrowserNotification.create(chrome.i18n.getMessage('fileEmpty')); + return; + } - file.fileName = decodeFileName(file.fileName); + BrowserNotification.create(chrome.i18n.getMessage('scanStarted') + file.fileName, file.id); + fileData = await ScanFile.getFileData(linkUrl, file.fileName); + } - file.extension = file.fileName.split('.').pop(); - file.canBeSanitized = file.extension && SANITIZATION_FILE_TYPES.indexOf(file.extension.toLowerCase()) > -1; + file.md5 = ScanFile.getMd5Hash(fileData); - if (file.size === null ) { - BrowserNotification.create(chrome.i18n.getMessage('fileEmpty')); - return; - } + if (file.fileName === '') { + file.fileName = file.md5; + } - if (file.size > MCL.config.fileSizeLimit) { - BrowserNotification.create(chrome.i18n.getMessage('fileSizeLimitExceeded')); - return; + this.scanFile(file, linkUrl, fileData, downloadItem, settings.useCore); } - file.statusLabel = ScanFile.getScanStatusLabel(); + /** + * Load a local file content. + * + * @param {string} localPath local file path + * @returns {Promise} + */ + async getDownloadedFile(localPath) { + return new Promise((resolve, reject) => { + let xhr = new XMLHttpRequest(); + xhr.responseType = 'arraybuffer'; + xhr.onreadystatechange = function () { + if (this.readyState === XMLHttpRequest.DONE) { + if (this.status === 0 && this.response === null) { + reject(chrome.i18n.getMessage('errorCors')); + } + + resolve(this.response); + } + }; + xhr.open('GET', localPath); + xhr.send(); + }); + } - await scanHistory.addFile(file); + /** + * Register callback that will run on scan complete with file data. + * + * @param {*} callback + */ + addOnScanCompleteListener(callback) { + ON_SCAN_COMPLETE_LISTENERS.push(callback); + } - let fileData = null; + /** + * Remove registered callback. + * + * @param {*} callback + */ + removeOnScanCompleteListener(callback) { + const index = ON_SCAN_COMPLETE_LISTENERS.indexOf(callback); - if (downloadItem) { - try { - fileData = await this.getDownloadedFile(downloadItem.localPath || 'file://' + downloadItem.filename); - BrowserNotification.create(chrome.i18n.getMessage('scanStarted') + file.fileName, file.id); - } - catch (e) { - BrowserNotification.create(e, file.id); - scanHistory.removeFile(file); - return; + if (index > -1) { + ON_SCAN_COMPLETE_LISTENERS.splice(index, 1); } } - else { - if (file.size === 0) { - BrowserNotification.create(chrome.i18n.getMessage('fileEmpty')); + + /** + * Call all registered listeners and pass the payload. + * + * @param {*} payload + */ + callOnScanCompleteListeners(payload) { + if (!ON_SCAN_COMPLETE_LISTENERS.length) { return; } - BrowserNotification.create(chrome.i18n.getMessage('scanStarted') + file.fileName, file.id); - fileData = await ScanFile.getFileData(linkUrl, file.fileName); - } - - file.md5 = ScanFile.getMd5Hash(fileData); - - if (file.fileName === '') { - file.fileName = file.md5; + for (let i=0; i} + */ + async handleFileScanResults(file, info, linkUrl, fileData, downloaded) { + if (info.scan_results) { + file.status = ScanFile.getScanStatus(info.scan_results.scan_all_result_i); + file.statusLabel = ScanFile.getScanStatusLabel(info.scan_results.scan_all_result_i); + } + file.sha256 = info.file_info.sha256; + file.dataId = info.data_id; + + if (file.useCore) { + file.scanResults = `${settings.coreUrl}/#/user/dashboard/processinghistory/dataId/${file.dataId}`; + const postProcessing = info.process_info && info.process_info.post_processing; + const sanitizationSuccessfull = postProcessing && postProcessing.sanitization_details && postProcessing.sanitization_details.description === 'Sanitized successfully.'; + const sanitized = postProcessing && postProcessing.actions_ran.indexOf('Sanitized') !== -1; + if (sanitizationSuccessfull || sanitized) { + const sanitizedFileURL = `${settings.coreUrl}/file/converted/${file.dataId}?apikey=${settings.coreApikey}`; + // verify if the user has access + if (await CoreClient.file.checkSanitized(sanitizedFileURL)) { + file.sanitizedFileURL = sanitizedFileURL; } - - file.dataId = response.data_id; - scanHistory.save(); - await this.startStatusPolling(file, linkUrl, fileData); - return; } - throw response.error; + } + else { + file.scanResults = `${MCL.config.mclDomain}/results#!/file/${file.dataId}/regular/overview`; + if (info.sanitized && info.sanitized.file_path && !Object.prototype.hasOwnProperty.call(file, 'sanitizedFileURL')) { + file.sanitizedFileURL = info.sanitized.file_path; + } } - this.handleFileScanResults(file, response, linkUrl, fileData, !!downloadItem); - } - catch (reject){ - console.error(reject); + await scanHistory.save(); + BrowserMessage.send({ + event: BROWSER_EVENT.SCAN_COMPLETE + }); + + let notificationMessage = file.fileName + chrome.i18n.getMessage('fileScanComplete'); + notificationMessage += (file.status === ScanFile.STATUS.INFECTED) ? chrome.i18n.getMessage('threatDetected') : chrome.i18n.getMessage('noThreatDetected'); + + BrowserNotification.create(notificationMessage, file.id, file.status === ScanFile.STATUS.INFECTED); + + this.callOnScanCompleteListeners({ + status: file.status, + downloaded, + fileData, + linkUrl, + name: file.fileName + }); } -} -/** - * - * @param localPath - * @returns {Promise} - */ -async function getDownloadedFile(localPath) { - return new Promise((resolve, reject) => { - let xhr = new XMLHttpRequest(); - xhr.responseType = 'arraybuffer'; - xhr.onreadystatechange = function () { - if (this.readyState === XMLHttpRequest.DONE) { - if (this.status === 0 && this.response === null) { - reject(chrome.i18n.getMessage('errorCors')); - } + /** + * + * @param {*} file file info + * @param {string} linkUrl file file url + * @param {*} fileData file content + * @param {boolean} downloaded flag for files that are already downloaded + * @returns {Promise.} + */ + async startStatusPolling(file, linkUrl, fileData, downloaded) { + let response; + + if (file.useCore) { + file.scanResults = `${settings.coreUrl}/#/user/dashboard/processinghistory/dataId/${file.dataId}`; + await scanHistory.save(); + response = await CoreClient.file.poolForResults(file.dataId, 3000); - resolve(this.response); - } - }; - xhr.open('GET', localPath); - xhr.send(); - }); -} -/** - * Register callback that will run on scan complete with file data - * @param {*} callback - */ -function addOnScanCompleteListener(callback) { - ON_SCAN_COMPLETE_LISTENERS.push(callback); -} + } + else { + file.scanResults = `${MCL.config.mclDomain}/results#!/file/${file.dataId}/regular/overview`; + await scanHistory.save(); + response = await MetascanClient.setAuth(apikeyInfo.apikey).file.poolForResults(file.dataId, 3000); + } -/** - * Remove registered callback - * @param {*} callback - */ -function removeOnScanCompleteListener(callback) { - const index = ON_SCAN_COMPLETE_LISTENERS.indexOf(callback); + if (response.error) { + return; + } - if (index > -1) { - ON_SCAN_COMPLETE_LISTENERS.splice(index, 1); - } -} -/** - * Call all registered listeners and pass the payload - * @param {*} payload - */ -function callOnScanCompleteListeners(payload) { - if (!ON_SCAN_COMPLETE_LISTENERS.length) { - return; + this.handleFileScanResults(file, response, linkUrl, fileData, downloaded); } - for (let i=0; i} - */ -async function handleFileScanResults(file, info, linkUrl, fileData, downloaded) { - file.status = ScanFile.getScanStatus(info.scan_results.scan_all_result_i); - file.statusLabel = ScanFile.getScanStatusLabel(info.scan_results.scan_all_result_i); - file.sha256 = info.file_info.sha256; - file.dataId = info.data_id; - file.scanResults = `${MCL.config.mclDomain}/results#!/file/${file.dataId}/regular/overview`; - - if (info.sanitized && info.sanitized.file_path && !Object.prototype.hasOwnProperty.call(file, 'sanitizedFileURL')) { - file.sanitizedFileURL = info.sanitized.file_path; - } + /** + * + * @param {*} file file information + * @param {string} linkUrl file url + * @param {*} fileData file content + * @param {*} downloadItem https://developer.chrome.com/extensions/downloads#type-DownloadItem + * @param {boolean} useCore use core API instead of cloud + */ + async scanFile(file, linkUrl, fileData, downloadItem, useCore) { + try { + file.useCore = useCore; + scanHistory.save(); - await scanHistory.save(); - this.browserMessage.send({ - event: BROWSER_EVENT.SCAN_COMPLETE - }); + let response = useCore + ? await this.scanWithCore(file, fileData) + : await this.scanWithCloud(file, fileData); - let notificationMessage = file.fileName + chrome.i18n.getMessage('fileScanComplete'); - notificationMessage += (file.status === ScanFile.STATUS.INFECTED) ? chrome.i18n.getMessage('threatDetected') : chrome.i18n.getMessage('noThreatDetected'); + if (!response.data_id) { + throw response; + } - BrowserNotification.create(notificationMessage, file.id, file.status === ScanFile.STATUS.INFECTED); + file.dataId = response.data_id; + scanHistory.save(); + await this.startStatusPolling(file, linkUrl, fileData, !!downloadItem); - this.callOnScanCompleteListeners({ - status: file.status, - downloaded, - fileData, - linkUrl, - name: file.fileName - }); -} + } catch (error) { + BrowserNotification.create(chrome.i18n.getMessage('scanFileError')); + _gaq.push(['exception', {exDescription: 'file-processor:scanFile' + JSON.stringify(error)}]); + } + } -/** - * - * @param file - * @param linkUrl - * @param fileData - * @returns {Promise.} - */ -async function startStatusPolling(file, linkUrl, fileData) { - if (!file.dataId){ - scanHistory.files.splice(scanHistory.files.indexOf(file), 1); - scanHistory.save(); - return; + /** + * Scan a file using Metadefender Core + * + * @param {*} file file information + * @param {*} fileData file content + */ + async scanWithCore(file, fileData) { + let response = await CoreClient.hash.lookup(file.md5); + + if (response[file.md5] === 'Not Found') { + response = await CoreClient.file.upload({ + fileData: fileData, + fileName: file.fileName, + rule: settings.coreRule + }); + } + + return response; } - file.scanResults = `${MCL.config.mclDomain}/results#!/file/${file.dataId}/regular/overview`; - await scanHistory.save(); + /** + * Scan a file using Metadefender Cloud + * + * @param {*} file file information + * @param {*} fileData file content + */ + async scanWithCloud(file, fileData) { + let response = await MetascanClient.setAuth(apikeyInfo.apikey).hash.lookup(file.md5); - let response = await MetascanClient.setAuth(apikeyInfo.apikey).file.poolForResults(file.dataId, 3000); + if (response.error && response.error.code === MetascanClient.ERROR_CODE.HASH_NOT_FOUND) { + response = await MetascanClient.setAuth(apikeyInfo.apikey).file.upload({ + fileName: file.fileName, + fileData, + sampleSharing: settings.shareResults, + canBeSanitized: file.canBeSanitized + }); + } - if (response.error) { - return; + return response; } - - this.handleFileScanResults(file, response, linkUrl, fileData); } + +export default new FileProcessor(); \ No newline at end of file diff --git a/app/scripts/common/persistent/apikey-info.js b/app/scripts/common/persistent/apikey-info.js index caaf313..e5f68f1 100644 --- a/app/scripts/common/persistent/apikey-info.js +++ b/app/scripts/common/persistent/apikey-info.js @@ -17,6 +17,8 @@ function ApikeyInfo() { feedLimit: null, paidUser: null, limitInterval: 'Daily', + maxUploadFileSize: null, + sandboxLimit: null, loggedIn: true, // methods @@ -64,7 +66,9 @@ async function save() { feedLimit: this.feedLimit, paidUser: this.paidUser, limitInterval: this.limitInterval, - loggedIn: this.loggedIn + maxUploadFileSize: this.maxUploadFileSize, + sandboxLimit: this.sandboxLimit, + loggedIn: this.loggedIn, }}); } @@ -78,6 +82,8 @@ function parseMclInfo(info) { this.feedLimit = info.limit_feed; this.paidUser = info.paid_user; this.limitInterval = info.time_interval; + this.maxUploadFileSize = info.max_upload_file_size; + this.sandboxLimit = info.limit_sandbox; } /** diff --git a/app/scripts/common/persistent/settings.js b/app/scripts/common/persistent/settings.js index 8be04b7..0048cdb 100644 --- a/app/scripts/common/persistent/settings.js +++ b/app/scripts/common/persistent/settings.js @@ -12,24 +12,25 @@ import BrowserMessage from './../browser/browser-message'; * @returns {{scanDownloads: boolean, shareResults: boolean, showNotifications: boolean, saveCleanFiles: boolean, init: init, merge: merge, save: save, load: load}} * @constructor */ -function Settings() { +const Settings = { + scanDownloads: true, + shareResults: true, + showNotifications: true, + saveCleanFiles: false, + safeUrl: false, + useCore: false, + coreUrl: '', + coreApikey: '', + coreRule: '', - return { - scanDownloads: true, - shareResults: true, - showNotifications: true, - saveCleanFiles: false, - safeUrl: false, + // methods + init: init, + merge: merge, + save: save, + load: load, +}; - // methods - init: init, - merge: merge, - save: save, - load: load, - }; -} - -export const settings = Settings(); +export const settings = Settings; /** * @@ -58,13 +59,12 @@ function merge(newData) { * @returns {Promise.} */ async function save(){ - await BrowserStorage.set({[MCL.config.storageKey.settings]: { - scanDownloads: this.scanDownloads, - shareResults: this.shareResults, - showNotifications: this.showNotifications, - saveCleanFiles: this.saveCleanFiles, - safeUrl: this.safeUrl - }}); + const settingKeys = ['scanDownloads', 'shareResults', 'showNotifications', 'saveCleanFiles', 'safeUrl', 'useCore', 'coreUrl', 'coreApikey', 'coreRule']; + const data = {}; + for (const key of settingKeys) { + data[key] = this[key]; + } + await BrowserStorage.set({[MCL.config.storageKey.settings]: data}); await BrowserMessage.send({event: BROWSER_EVENT.SETTINGS_UPDATED}); } diff --git a/app/scripts/common/scan-file.js b/app/scripts/common/scan-file.js index 00248f3..19ef10f 100755 --- a/app/scripts/common/scan-file.js +++ b/app/scripts/common/scan-file.js @@ -60,14 +60,26 @@ async function download(link, fileData, fileName) { }); } +/** + * Checks if an URL points to a sanitized file. + * + * @param {string} url a file url + * @returns {boolean} `true` if the url provided is of a sanitized file + */ function isSanitizedFile(url) { const urlLow = url.toLowerCase(); + // metadefender cloud sanitized files for (let bucket of MCL.config.sanitizationBuckets) { if (urlLow.indexOf(bucket) > -1 ) { return true; } } + + // metadefender core sanitized files + if (urlLow.indexOf('/file/converted/') > -1 && urlLow.indexOf('?apikey=') > -1) { + return true; + } } function getFileSize(url, filename){ diff --git a/app/scripts/extension/settings.controller.js b/app/scripts/extension/settings.controller.js index 664126b..c36100f 100755 --- a/app/scripts/extension/settings.controller.js +++ b/app/scripts/extension/settings.controller.js @@ -1,11 +1,12 @@ 'use strict'; import 'chromereload/devonly'; +import CoreClient from './../common/core-client'; -settingsController.$inject = ['$scope', '$timeout', 'browserTranslate', 'browserExtension', 'settings', 'apikeyInfo', 'CONFIG', 'EVENT']; +settingsController.$inject = ['$scope', '$timeout', 'browserTranslate', 'browserExtension', 'browserNotification', 'settings', 'apikeyInfo', 'CONFIG', 'EVENT']; /* @ngInject */ -function settingsController($scope, $timeout, browserTranslate, browserExtension, settings, apikeyInfo, CONFIG, EVENT){ +function settingsController($scope, $timeout, browserTranslate, browserExtension, browserNotification, settings, apikeyInfo, CONFIG, EVENT){ let vm = this; // use vm instead of $scope vm.title = 'settingsController'; @@ -16,9 +17,34 @@ function settingsController($scope, $timeout, browserTranslate, browserExtension vm.apikeyInfo = apikeyInfo; vm.settings = settings; - + vm.coreSettings = { + useCore: false, + apikey: { + value: '', + valid: undefined, + groupClass: {}, + iconClass: {}, + }, + url: { + value: '', + valid: undefined, + groupClass: {}, + iconClass: {}, + }, + rule: { + value: '' + }, + scanRules: [], + }; + vm.settingsChanged = settingsChanged; vm.openExtensionSettings = openExtensionSettings; + vm.validateCoreSettings = validateCoreSettings; + + CoreClient.configure({ + pollingIncrementor: CONFIG.scanResults.incrementor, + pollingMaxInterval: CONFIG.scanResults.maxInterval, + }); activate(); @@ -34,27 +60,67 @@ function settingsController($scope, $timeout, browserTranslate, browserExtension await vm.apikeyInfo.init(); await vm.settings.init(); + vm.coreSettings.useCore = vm.settings.useCore; + vm.coreSettings.apikey.value = vm.settings.coreApikey || ''; + vm.coreSettings.url.value = vm.settings.coreUrl || ''; + vm.coreSettings.rule.value = vm.settings.coreRule || ''; + vm.isAllowedFileAccess = await browserExtension.isAllowedFileSchemeAccess(); if (!vm.isAllowedFileAccess) { settingsChanged('scanDownloads'); } - $scope.$apply(); + if (vm.coreSettings.useCore) { + vm.validateCoreSettings(); + } + + $timeout(() => { + initDropdowns(); + $scope.$apply(); + }); + } + + function initDropdowns() { + vm.coreSettings.rule.value = vm.settings.coreRule || vm.coreSettings.scanRules[0]; } - async function settingsChanged(property) { - if (property === 'scanDownloads' && !vm.settings[property]) { + async function settingsChanged(key) { + if (key === 'coreSettings') { + $scope.coreSettingsForm.$setPristine(); + vm.settings.coreApikey = vm.coreSettings.apikey.value; + vm.settings.coreUrl = vm.coreSettings.url.value; + if (await vm.validateCoreSettings()) { + vm.settings.useCore = vm.coreSettings.useCore; + vm.settings.coreRule = vm.coreSettings.rule.value; + browserNotification.create(browserTranslate.getMessage('coreSettingsSavedNotification'), 'info'); + } + else { + vm.settings.useCore = false; + browserNotification.create(browserTranslate.getMessage('coreSettingsInvalidNotification'), 'info'); + } + await vm.settings.save(); + $timeout(() => { $scope.$apply(); }); + return; + } + + if (key === 'useCore') { + vm.coreSettings.useCore = !vm.coreSettings.useCore; + if (!vm.coreSettings.useCore || await vm.validateCoreSettings()) { + vm.settings.useCore = vm.coreSettings.useCore; + } + } + else if (key === 'scanDownloads' && !vm.settings[key]) { vm.isAllowedFileAccess = await browserExtension.isAllowedFileSchemeAccess(); - vm.settings[property] = vm.isAllowedFileAccess; + vm.settings[key] = vm.isAllowedFileAccess; } else { - vm.settings[property] = !vm.settings[property]; + vm.settings[key] = !vm.settings[key]; } - vm.settings.save(); - - _gaq.push(['_trackEvent', MCL.config.gaEventCategory.name, MCL.config.gaEventCategory.action.settingsChanged, property, (vm.settings[property] ? 'enabled' : 'disabled')]); + await vm.settings.save(); $timeout(() => { $scope.$apply(); }); + + _gaq.push(['_trackEvent', MCL.config.gaEventCategory.name, MCL.config.gaEventCategory.action.settingsChanged, key, (vm.settings[key] ? 'enabled' : 'disabled')]); } function refreshSettings() { @@ -74,6 +140,73 @@ function settingsController($scope, $timeout, browserTranslate, browserExtension } }); } + + /** + * // settings.coreApikey; + * // settings.coreUrl; + * // settings.coreWorkflow; + * + * vm.settings.coreApikey + */ + async function validateCoreSettings() { + if (!vm.coreSettings.apikey.value || !vm.coreSettings.url.value) { + return; + } + + CoreClient.configure({ + apikey: vm.coreSettings.apikey.value, + endpoint: vm.coreSettings.url.value, + }); + + try { + let result = await CoreClient.version(); + result = await CoreClient.rules(''); + vm.coreSettings.scanRules = result.map(r => r.name); + + setInputState(vm.coreSettings.apikey, 'success'); + setInputState(vm.coreSettings.url, 'success'); + + $timeout(() => { $scope.$apply(); }); + return true; + } catch (error) { + if (error.statusCode === 403) { + setInputState(vm.coreSettings.apikey, 'error'); + $timeout(() => { $scope.$apply(); }); + return false; + } + setInputState(vm.coreSettings.url, 'error'); + $timeout(() => { $scope.$apply(); }); + return false; + } + } } export default settingsController; + +/** + * + * @param {*} element + * @param {string} state input state: 'success' | 'error' | undefined + */ +function setInputState(element, state) { + switch (state) { + case 'success': { + element.valid = true; + element.groupClass = {'has-success': true}; + element.iconClass = {'icon-ok': true}; + break; + } + case 'error': { + element.valid = false; + element.groupClass = {'has-error': true}; + element.iconClass = {'icon-cancel': true}; + break; + } + default: { + element.valid = undefined; + element.groupClass = {}; + element.iconClass = {}; + } + } +} + diff --git a/app/styles/extension.scss b/app/styles/extension.scss index 7ba378a..3fdfca7 100755 --- a/app/styles/extension.scss +++ b/app/styles/extension.scss @@ -94,7 +94,7 @@ body { } &:after { - content: '\e801'; + content: '\e805'; font-family: mcl-ext-icons; position: absolute; top: 9px; @@ -195,6 +195,27 @@ body { .list-group-item { padding: $padding $padding $padding + 10; + &.useCore { + + .form-horizontal { + padding-top: 15px; + padding-left: 60px; + + label.control-label { + font-weight: 400; + } + } + + .btn { + line-height: 1px; + height: 38px; + } + + p.rule-label { + padding: 7px 12px; + } + } + sub { padding-left: 60px; @@ -246,7 +267,7 @@ body { .icon-ok { display: none; - color: #fff; + color: $colorWhite; &:before { margin-top: 3px; @@ -303,18 +324,28 @@ body { vertical-align: middle; position: relative; + &:first-child { + display: flex; + + .scan-type { + padding: 4px 10px 0 0; + font-size: 2rem; + color: $colorGreyDarker; + } + } + &.action-column { padding: 0; text-align: center; } - .icon-trash-empty { + .icon-trash { display:none; } } &:hover { - .icon-trash-empty { + .icon-trash { display: inline-block; } } From bc62c07c032c6d8a9b8b952c869900960faa056f Mon Sep 17 00:00:00 2001 From: Andrei Domsa Date: Wed, 25 Sep 2019 16:04:52 +0300 Subject: [PATCH 3/5] Hook updating version --- app/manifest.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/manifest.json b/app/manifest.json index ab51548..77e3dee 100755 --- a/app/manifest.json +++ b/app/manifest.json @@ -2,7 +2,7 @@ "name": "__MSG_appName__", "short_name": "__MSG_appShortName__", "description": "__MSG_appDescription__", - "version": "3.8.1", + version": "3.9.0", "manifest_version": 2, "default_locale": "en", "icons": { diff --git a/package.json b/package.json index 4ad4b9c..c429813 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "opswat-file-security-chrome", "private": true, - "version": "3.8.1", + version": "3.9.0", "description": "Scan files and downloads with OPSWAT File Security for Chrome", "repository": { "type": "git", From 611259aa1a9940c1501351bdc357fc417c7d5ed8 Mon Sep 17 00:00:00 2001 From: Andrei Domsa Date: Wed, 25 Sep 2019 16:12:53 +0300 Subject: [PATCH 4/5] fix update version hook --- app/manifest.json | 2 +- git_hooks/pre-flow-hotfix-start | 4 ++-- git_hooks/pre-flow-release-start | 4 ++-- package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/manifest.json b/app/manifest.json index 77e3dee..8a0018b 100755 --- a/app/manifest.json +++ b/app/manifest.json @@ -2,7 +2,7 @@ "name": "__MSG_appName__", "short_name": "__MSG_appShortName__", "description": "__MSG_appDescription__", - version": "3.9.0", + "version": "3.9.0", "manifest_version": 2, "default_locale": "en", "icons": { diff --git a/git_hooks/pre-flow-hotfix-start b/git_hooks/pre-flow-hotfix-start index dde0a46..2b7c97d 100755 --- a/git_hooks/pre-flow-hotfix-start +++ b/git_hooks/pre-flow-hotfix-start @@ -3,7 +3,7 @@ VERSION=$1 echo "Incrementing version to $VERSION" -sed -i "s/\"version\": \".*\"/version\": \"${VERSION}\"/g" package.json -sed -i "s/\"version\": \".*\"/version\": \"${VERSION}\"/g" app/manifest.json +sed -i "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/g" package.json +sed -i "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/g" app/manifest.json git commit -a -m "Hook updating version" \ No newline at end of file diff --git a/git_hooks/pre-flow-release-start b/git_hooks/pre-flow-release-start index dde0a46..2b7c97d 100755 --- a/git_hooks/pre-flow-release-start +++ b/git_hooks/pre-flow-release-start @@ -3,7 +3,7 @@ VERSION=$1 echo "Incrementing version to $VERSION" -sed -i "s/\"version\": \".*\"/version\": \"${VERSION}\"/g" package.json -sed -i "s/\"version\": \".*\"/version\": \"${VERSION}\"/g" app/manifest.json +sed -i "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/g" package.json +sed -i "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/g" app/manifest.json git commit -a -m "Hook updating version" \ No newline at end of file diff --git a/package.json b/package.json index c429813..92a6577 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "opswat-file-security-chrome", "private": true, - version": "3.9.0", + "version": "3.9.0", "description": "Scan files and downloads with OPSWAT File Security for Chrome", "repository": { "type": "git", From 950ee633e2affc8f921cfbe4fcbbed845c7656ca Mon Sep 17 00:00:00 2001 From: Andrei Domsa Date: Wed, 25 Sep 2019 17:10:33 +0300 Subject: [PATCH 5/5] updated git flow hooks --- git_hooks/pre-flow-hotfix-start | 3 +++ git_hooks/pre-flow-release-start | 3 +++ 2 files changed, 6 insertions(+) diff --git a/git_hooks/pre-flow-hotfix-start b/git_hooks/pre-flow-hotfix-start index 2b7c97d..ac9a520 100755 --- a/git_hooks/pre-flow-hotfix-start +++ b/git_hooks/pre-flow-hotfix-start @@ -3,6 +3,9 @@ VERSION=$1 echo "Incrementing version to $VERSION" +git checkout customer +git pull + sed -i "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/g" package.json sed -i "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/g" app/manifest.json diff --git a/git_hooks/pre-flow-release-start b/git_hooks/pre-flow-release-start index 2b7c97d..ddf73b4 100755 --- a/git_hooks/pre-flow-release-start +++ b/git_hooks/pre-flow-release-start @@ -3,6 +3,9 @@ VERSION=$1 echo "Incrementing version to $VERSION" +git checkout master +git pull + sed -i "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/g" package.json sed -i "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/g" app/manifest.json