I would like to make a soft integration for Subresource Integrity attributes, so be sure that I did not break the application, but only to show a warning that I need to fix some places.
Is there an option to do so?
Secure approach
If you need some kind of flexibility, then you should use a fallback mechanism - loading required resource from another URL. Probability that two different URL's will be hacked at the same time is a lot smaller compared to hacking just one resource. Fallback doesn't violate site security, because you must trust your known-good sources which you use in your code. If your resource is a Javascript - you can use a noncanonical-src attribute for a fallback too.
Insecure approach
Now, if you really, really want a user to break server and/or client security by forcing compromised resource load - at least ask a user if he/she takes responsibility by doing so. Of course this will still be a stupid thing, it's like asking "Would you like to run a virus in your computer ?". I bet nobody would like to say YES. Anyway, here is the code, which does asking these type of questions:
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<script>
function loadResource(path) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var cs = CryptoJS.SHA256(this.responseText);
if (btoa(cs) == 'NjBiMTllNWRhNmE5MjM0ZmY5MjIwNjY4YTVlYzExMjVjMTU3YTI2ODUxMzI1NjE4OGVlODBmMmQyYzhkOGQzNg==' ||
confirm('Bootstrap is NOT the latest version 4.3.1, load anyway ?')
) {
var link = document.createElement('link');
link.rel = "stylesheet";
link.href = path;
document.head.appendChild(link);
}
else {
var err = document.getElementById('error');
err.title = "Component version error !";
err.innerHTML = ' ⚠️';
}
}
};
xhttp.open("GET", path, true);
xhttp.send();
}
loadResource(
//'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' // newest boostrap
'https://stackpath.bootstrapcdn.com/twitter-bootstrap/2.0.4/css/bootstrap-combined.min.css' // old legacy
);
</script>
DEMO
I do not recommend only displaying warnings when the SRI-Hashes don't match. When see the warning as a User, it's already too late and potentially malicious scripts were executed on your machine.
However, you can implement your desired behaviour using the ServiceWorker-API and something like <script data-integrity="xxxxxxxx">. For that, you'd want to:
Register a new ServiceWorker
Listen to the fetch event
[Client.postMessage] the targetURL to your Parent
Get script integrity hash by targetURL $('script[src=event.data.targetURL]').attr('data-integrity')
and push it into the client using Worker.postMessage
hash the response using e.G. cryptojs.sha256
match the hashes inside the worker
If the hashes match, return the response. If they don't match, return the response and use Client.postMessage again to trigger a warning.
Related
There aren't many examples demonstrating indexedDB in a ServiceWorker yet, but the ones I saw were all structured like this:
const request = indexedDB.open( 'myDB', 1 );
var db;
request.onupgradeneeded = ...
request.onsuccess = function() {
db = this.result; // Average 8ms
};
self.onfetch = function(e)
{
const requestURL = new URL( e.request.url ),
path = requestURL.pathname;
if( path === '/test' )
{
const response = new Promise( function( resolve )
{
console.log( performance.now(), typeof db ); // Average 15ms
db.transaction( 'cache' ).objectStore( 'cache' ).get( 'test' ).onsuccess = function()
{
resolve( new Response( this.result, { headers: { 'content-type':'text/plain' } } ) );
}
});
e.respondWith( response );
}
}
Is this likely to fail when the ServiceWorker starts up, and if so what is a robust way of accessing indexedDB in a ServiceWorker?
Opening the IDB every time the ServiceWorker starts up is unlikely to be optimal, you'll end up opening it even when it isn't used. Instead, open the db when you need it. A singleton is really useful here (see https://github.com/jakearchibald/svgomg/blob/master/src/js/utils/storage.js#L5), so you don't need to open IDB twice if it's used twice in its lifetime.
The "activate" event is a great place to open IDB and let any "onupdateneeded" events run, as the old version of ServiceWorker is out of the way.
You can wrap a transaction in a promise like so:
var tx = db.transaction(scope, mode);
var p = new Promise(function(resolve, reject) {
tx.onabort = function() { reject(tx.error); };
tx.oncomplete = function() { resolve(); };
});
Now p will resolve/reject when the transaction completes/aborts. So you can do arbitrary logic in the tx transaction, and p.then(...) and/or pass a dependent promise into e.respondWith() or e.waitUntil() etc.
As noted by other commenters, we really do need to promisify IndexedDB. But the composition of its post-task autocommit model and the microtask queues that Promises use make it... nontrivial to do so without basically completely replacing the API. But (as an implementer and one of the spec editors) I'm actively prototyping some ideas.
I don't know of anything special about accessing IndexedDB from the context of a service worker via accessing IndexedDB via a controlled page.
Promises obviously makes your life much easier within a service worker, so I've found using something like, e.g., https://gist.github.com/inexorabletash/c8069c042b734519680c to be useful instead of the raw IndexedDB API. But it's not mandatory as long as you create and manage your own promises to reflect the state of the asynchronous IndexedDB operations.
The main thing to keep in mind when writing a fetch event handler (and this isn't specific to using IndexedDB), is that if you call event.respondWith(), you need to pass in either a Response object or a promise that resolves with a Response object. As long as you're doing that, it shouldn't matter whether your Response is constructed from IndexedDB entries or the Cache API or elsewhere.
Are you running into any actual problems with the code you posted, or was this more of a theoretical question?
I have the working code which sets and clears cookie (remembers div's style) on click:
var originalAttributes = $('.aaa').attr('style');
$('.aaa').each(function(){
var d = $(this),
id = d.attr('id'),
storedStyle = $.cookie('aaaStyle' + id);
if (storedStyle != undefined){ //style stored
d.attr('style', storedStyle);
}
});
//mouse event functions for class="aaa"
$('#save').click(function () {
$('.aaa').each(function(){
var d = $(this),
id = d.attr('id'),
style = d.attr('style');
if (style != originalAttributes){ //style changed
$.cookie('aaaStyle' + id, style, { expires: 30 });
}
});
});
$('#clear').click(function () {
// unset changes
$('.aaa').attr('style',originalAttributes).each(function(){
var d = $(this),
id = d.attr('id');
$.cookie('aaaStyle' + id, null);
});
});
http://jsfiddle.net/z8KuE/31/
Only problem which occurs with this is when I have to handle a lot of divs of the same class - cookie size can get to 500kb or more. Browsers supports only 4kb per cookie.
So the question is - how can this problem be avoided with this function and with the jquery cookie plugin? - gzip or / and splitting the cookie in small enough parts?
(in either way, it would be good to have some sort of compression in order to speed up the performance (if possible - but if not, doesn't matter))
edit: how this same "save - clear" functionality can be achieved with the local storage?
edit2: solved by user2111737 (http://jsfiddle.net/z8KuE/33/) - uses local storage instead of cookie and works without cookie plugin.
if you don't need to access it on server side or eventually it's possible to manually send this data to server with xmlhttprequest I think you should rather try localStorage, eventually sessionStorage instead of cookies, then you have 20mb (200 in IE but shared with other sites). About compression - you can think about custom format and rebuild html code using stored data in fly - eg. i doubt this class can be absolutely anything - i guess it could be saved as number - or even better - one character. It gives you 255 classes saved as one sign
Our corporate wiki is Mediawiki. I have no problem to put iframe into my site to refer for some article on wiki.
But my own site have a lot of widgets and own style. I don't want to include Mediawiki navigation/search/login widgets, logo image.
Is it possible and how to get Mediawiki page contents without widgets (only article body)?
Yes, it is. You'll probably want to use the action=render url parameter, for example: http://en.wikipedia.org/w/index.php?action=render&title=Main_Page. Note that the stylesheets from the wiki aren't included, so you'll need to copy the relevant rules to your site's css files. See also this.
Thank waldir for answer!
After asking question I perform own research and end with code:
window.onload = function() {
httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState !== 4) {
console.log("Not ready, code: %o", httpRequest.readyState);
return;
}
if (httpRequest.status !== 200) {
console.log("Server error: %o", httpRequest.status);
return;
}
var json = JSON.parse(httpRequest.responseText);
console.log("json: %o", json);
var wiki = json.query.pages["1"].revisions[0]["*"];
console.log("wiki: %o", wiki);
var html = InstaView.convert(wiki);
console.log("html: %o", html);
document.getElementById('area').innerHTML = html;
};
var url = 'https://wiki.evil-company.com/api.php?action=query&prop=revisions&format=json&titles=Main_page&rvprop=timestamp|user|comment|content';
httpRequest.open('GET', url, true);
httpRequest.send(null);
}
Here I use https://github.com/cscott/instaview/blob/master/main.js project which is enhanced http://en.wikipedia.org/wiki/User:Pilaf to transform json output to HTML on browser side.
The reason for this code because our wiki is old or misconfigured and action=render is not available. But I trap into cross-domain scripting issue so I think that iframe with action=render is better solution.
See also How do you grab an article including the links in a usable format?
Another suggestion to use action=parse (http://en.wikipedia.org/w/api.php?action=parse&title=Linux) lead to warning:
You are looking at the HTML representation of the XML format.
HTML is good for debugging, but is unsuitable for application use.
Specify the format parameter to change the output format.
UPDATE
Perfect solution just append query action=render to any valid wiki URL like:
http://en.wikipedia.org/wiki/Linux?action=render
Im looking for the equivalent of "tail -f" that runs in a browser using html5 or javascript.
A solution would need a client side code written in HTML5/websockets/javascript and a back-end server side application. Im looking for one in c# but i'm willing to rewrite it from php or python.
This is the only thing that i've seen that comes close is
http://commavee.com/2007/04/13/ajax-logfile-tailer-viewer/
However, modern browsers have WebSockets which makes the problem much simpler.
http://www.websocket.org/echo.html
Ideally, I would like to have some of the capabilities of BareTail
http://www.baremetalsoft.com/baretail/
Such as Color Coding of lines, sorting and multi-file tabbing.
I have located a similar posting where someone is looking for windows based log file programs
https://stackoverflow.com/questions/113121/best-tail-log-file-visualization-freeware-tool
Anyone have any suggestions?
It is not exactly like tail but the live logs feature of https://log4sure.com does allow you to monitor your client side logs realtime. You would have to setup and do the logs appropriately as you would do for tailing, but you can see all the logs with extra information about your client, example browser, os, country etc. You can also create your own custom logs to log stuff. Checkout the demo on the site to get a better idea.
The setup code is really easy, and the best part is, its free.
// set up
var _logServer;
(function() {
var ls = document.createElement('script');
ls.type = 'text/javascript';
ls.async = true;
ls.src = 'https://log4sure.com/ScriptsExt/log4sure-0.1.min.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ls, s);
ls.onload = function() {
// use your token here.
_logServer = new LogServer("use-your-token-here");
};
})();
// example for logging text
_logServer.logText("your log message goes here.")
// example for logging error
divide = function(numerator, divisor) {
try {
if (parseFloat(value) && parseFloat(divisor)) {
throw new TypeError("Invalid input", "myfile.js", 12, {
value: value,
divisor: divisor
});
} else {
if (divisor == 0) {
throw new RangeError("Divide by 0", "myfile.js", 15, {
value: value,
divisor: divisor
});
}
}
} catch (e) {
_logServer.logError(e.name, e.message, e.stack);
}
}
// another use of logError in window.onerror
// must be careful with window.onerror as you might be overwriting some one else's window.onerror functionality
// also someone else can overwrite window.onerror.
window.onerror = function(msg, url, line, column, err) {
// may want to check if url belongs to your javascript file
var data = {
url: url,
line: line,
column: column,
}
_logServer.logError(err.name, err.message, err.stack, data);
};
//example for custom logs
var foo = "some variable value";
var bar = "another variable value";
var flag = "false";
var temp = "yet another variable value";
_logServer.log(foo, bar, flag, temp);
While I wish it had better JSON object prettification for live tailing and historical logs, the following JS client works and supports your server-side requirement also:
https://github.com/logentries/le_js/wiki/API
<html lang="en">
<head>
<title>Your page</title>
<script src="/js/le.min.js"></script>
<script>
// Set up le.js
LE.init('YOUR-LOG-TOKEN');
</script>
</head>
.....
<script>
// log something
LE.log("Hello, logger!");
</script>
Personally to get the above code to work however, I've had to add the following line of code just above LE.init('YOUR-LOG-TOKEN'):
window.LEENDPOINT = 'js.logentries.com/v1'
.. Alternatively, Loggly may be a fit as well: https://www.loggly.com/docs/javascript/
Gurus of SO
I have posted a web app to the iOS Home Screen & want to not have to login each time the app opens up. So I am trying to push the cookie into LocalStorage.
I am using the following code to try to store my cookies in LocalStorage for a mobile web app (code copied from iphone web app ruby gem). But somehow its not working. Any suggestions?
Thank you.
<script type="text/javascript">
(function(){
var RESEND_REQUEST = {{RESEND}};
function isFullScreen(){
return navigator.userAgent.match(/WebKit.*Mobile/) &&
!navigator.userAgent.match(/Safari/);
}
if(isFullScreen()){
if(document.cookie == ''){
var storedValues = localStorage.getItem('__cookie__');
if(storedValues){
var values = storedValues.split(';');
for(var i=0; i < values.length; i++)
document.cookie = values[i];
}
document.cookie = '_cookieset_=1';
if(RESEND_REQUEST){
window.location.reload();
}
}
var lastCookie = null;
setInterval(function(){
if(lastCookie != ''+document.cookie){
lastCookie = ''+document.cookie;
localStorage.setItem('__cookie__', ''+document.cookie);
}
},1000);
}
})()
There are couple thing that does fit in the above code
1. if(document.cookie == '')
The above statement not always suppose return true even when you are opening your web_app from iOS Home Screen for the first time i.e the document.cookie does contain some value (junk though but still) even opening from Home screen(atleast what I found). I urge you to prompt the same with alert
Something like alert(document.cookie) before running into the above mentionif clause
If yes(document.cookie does contain some value) then I guess you need to fix the above if clause something like this
> if(!document.cookie.match(/_session_id/) ) {
> // Rest of the code goes here
> }
if your using ActiveRecord::Base.session_store
or
> if (!document.cookie.match(/{{YOUR SESSION KEY}}/) {
> // Rest of the code goes here
> }
your Session Key if using Cookie Store "the following key can be found my looking at the config/initializer/session_store.rb file
2. As notice the below code
localStorage.setItem('__cookie__', ''+document.cookie)
does make sense when reading though it but there is twist to it
one would except the document.cookie to contain cookie for the application maintained
and stored by the browser but as I notice that document.cookie does not turn out to be same
e.g browser stored the following cookie for my application
"__cookieset=1;KBD=0en-3;_session_id=896c455928f3dd9e7bb0b660efb7063c"
but when inspected the document.cookie I found it to be contain
"__cookieset=1;KBD=0en-3;"
Notice that document.cookie doesnot contain "_session_id=896c455928f3dd9e7bb0b660efb7063c"
Which is must as It used by various authorization gem(devise or authlogic) to determine whether the current user has a valid session ?
so I request you store the cookie from the request object obtain from Rack::Request.new(env)
into the localStorage
3. The middleware placement make sure your placing middleware at right place.
If your using ActiveRecord::Base.session_store I guess the patch code of the same gem can be found here solve your purpose