Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
DSW allows you to enable and use Service Workers in a much easier way, also helping you to create and maintain your Progressive Web Apps working offline.
You will simply have to set up how your service worker will handle requests in a JSON file. Read the commented JSON example or the documentation and examples below.
If you are starting from scratch and want to see it working right away, you can use the content inside of /boilerplate
.
You can access this page and see a live demo of DSW working. After loading the page the first time, it will install the service worker. When opening it the second time, it will cache everything according to the defined rules (described in each block and link). You can then go offline and reload the page to validate it. Dynamic Service Worker demo
Read the commented json configuration file: https://naschq.github.io/dsw/config-example.html
It's a Node.js program that you may install globally:
npm install -g dsw
Or locally:
npm install dsw --save
DSW will look for a file called dswfile.json
, just like gulp or grunt do.
So:
dswfile.json
.cd path-to-your-project
touch dswfile.json
You will use your prefered editor to make changes to this file later.
index.html
file, in the head
element:<link rel="manifest" href="/webapp-manifest.json" />
<meta name="theme-color" content="#color">
<script src="dsw.js"></script>
<script>
DSW.setup()
.then(() => {
// inform the user your page works offline, now!
// maybe, consider reloading the page automaticaly
})
.catch(() => {
// do something if the page will not work offline
// or if the current browser does not support it
});
</script>
dsw
You can also use dsw path-to-your/project
.
This will generate the webapp.manifest
and dsw.js
files in your project's root directory.
dsw
again, so it will generate the updated service worker file.manifest
(if not there, already) and the dsw.js
file.To do so, if you installed it globally:
dsw path-to-your/project
If you installed locally, though:
node node_modules/dsw/bin [path-to-your-project]
This second example is specially useful if you intend to run it in a stand alone project or want to trigger it using a script in your package.json
file.
From now on, let's work as if you had installed it globally in our examples.
Now, let's set up your project's offline configuration.
When you change something in your dswfile.json
, you shall re-execute the command above.
Open the dswfile.json
in the root of your project and let's add some content like this:
{
"dswVersion": 1.0,
"applyImmediately": true,
"dswRules": {
"yourRuleName": {
"match": { },
"apply": { }
}
}
}
That's it! You may have many rules.
Reminding that applyImmediately
is optional. It will replace the previously registered service worker as soon as the new one loads.
If you want to enable and use push notifications, you can set:
{
"dswVersion": 1.0,
"applyImmediately": true,
"notification": {
"auto": false,
"service": "GCM",
"senderId": "your-project-id",
"dataSrc": "http://where.to/get-your/notification-data",
"dataPath": "notification"
},
"dswRules": {
/* ... */
}
}
Here, dataSrc
is the path or service where dsw will find the structure for your notification.
And dataPath
is an optional path for it. For example, the dataSrc request could return:
{
"title": "The title",
"icon": "path-to-icon",
"body": "The message itself"
}
In this case, dataPath
would not be provided. But:
{
"notification: {
"title": "The title",
"icon": "path-to-icon",
"body": "The message itself"
}
}
For this case, you can say that the dataPath
is "notification".
You can also provide the static information for notifications, like so:
{
"dswVersion": 1.0,
"applyImmediately": true,
"notification": {
"auto": false,
"service": "GCM",
"senderId": "your-project-id",
"title": "IMPORTANT",
"body": "There is an update in our page",
"icon": "path-to-icon"
},
"dswRules": {
/* ... */
}
}
In this case, you could use a more generic message simply to call your user to your page.
The auto
property is false by default, but if true, will ask for the users permission (to trigger notifications) as soon as the service worker gets registered.
NOTE: By now, only Google's GCM service is supported. You can use it with Firebase or Google Console, for example, to trigger new push notifications.
The match
property accepts an Object or an Array or objects with the following configuration:
When used as an object, multiple properties are used as "AND". For exampe:
match: {
extension: ['html', 'htm'],
status: [404, 500]
}
Will match requests with a status
equals to 404 or 500, AND with an extension of html or htm
.
While:
match: [
{ extension: ['html', 'htm'] },
{ path: 'some-dir\/' }
]
Will match all requests with an extension of html or htm
, OR in the some-dir/
path (no matter the extension, then).
Please notice that requests for different domains will become opaque
.
This means they will work, but may be cached with a bad status. This avoids the famous CORS errors, but exposes you to the chances of having them with the wrong cached data(if it failed in the first time the user loaded it).
The strategy tells DSW how to deal with different situations for a request lifecycle. It may be:
The apply
property for each rule is used when a request matches the match
requirements.
It may be:
request
(will go for the network, and if it fails, will output an empty string) or ignore
(will always output an empty string).DSW will treat the cache layer for you.
Pass to the cache object in your apply definition, an object containing:
You can also define cache: false
. This will force the request not to be cached.
Seens silly, but is useful when you want an exception for your cached data.
Expires is a number in mileseconds or a string with the pattern:
xs
xm
xh
xd
xw
xM
xy
By default, caches wont expire...ever! Only when the cache version changes.
When expired, DSW will look for the up to date content and will update it into the cache.
Although, if it fails retrieving the updated data, it will still use the previously cached data, until it manages to get the updated content.
Some times, you will request a JSON and IndexedDB is the best way to store it.
To do so, you will use the indexedDB
action in your apply
rule.
Pass an object containing the following:
Indexes may be a String or an object containing:
unique
or multiEntry
)For example:
"apply": {
"indexedDB": {
"name": "userData",
"version": "3",
"key": "id",
"indexes": [
"age",
{
"name": "twitter",
"path": "twitter",
"options": {
"unique": true
}
}
]
}
}
In this example, we will have three indexes: age, twitter and id (created automatically as it is the key).
If you DO NOT want to cache your json stored in IndexedDB, set cache: false
in your rule/apply configuration.
How it works
You may be wondering how it caches your data.
Well, it uses the cacheApi
to store as requests, only your keys. When you try to use it, it will use these ids to find the stored data you want, in your indexedDB.
This way, you can access the information in your IndexedDB by yourself, while your requests will automatically deal with it, too.
Yes, you can debug your configuration and trace requests!
The API for that is quite simple and very powerful.
DSW.trace('/some/matching-pattern', function(data){
console.log(data);
});
This is it. Now, any request that matches /some/matching-pattern
will be sent to your callback function with all the trace information.
This data includes all the steps and different states your requests have been through. This way you validate and debug your rules.
Using both match
and apply
, we can do a lot of things.
Don't forget to re-run dsw path-to-project
whenever you made a change to your dswfile.js
file.
Add this to your dswfile.js
:
{
"dswVersion": 2.2,
"dswRules": {
"notFoundPages": {
"match": {
"status": [404],
"extension": ["html"]
},
"apply": {
"fetch": "/my-404-page.html"
}
}
}
}
Add this to your dswfile.js
:
{
"dswVersion": 2.2,
"dswRules": {
"notFoundPages": {
"match": {
"status": [404],
"extension": ["png", "jpg", "jpeg", "gif"]
},
"apply": {
"fetch": "/my-404-page.html"
}
}
}
}
Create a my-404-page.html
with any content.
Now, access in your browser, first, the index.html
file(so the service worker will be installed), then any url replacing the index.html
string, and you will see your my-404-page.html
instead.
Let's see an example of requests being cached (by default, all requests are cached unless you use cache: false
):
{
"dswVersion": 2.2,
"dswRules": {
"myCachedImages": {
"match": {
"extension": ["png", "jpg", "gif"]
},
"apply": {
"cache": {
"name": "my-cached-images",
"version": 1
}
}
}
}
}
Let's see an example of requests being cached for all images except one specific image:
{
"dswVersion": 2.2,
"dswRules": {
"myNotCachedImage": {
"match": {
"path": "\/images\/some-specific-image"
},
"apply": {
"cache": false
}
},
"myCachedImages": {
"match": {
"extension": ["png", "jpg", "gif"]
},
"apply": {
"cache": {
"name": "my-cached-images",
"version": 1
}
}
}
}
}
You may want to redirect requests some times, like so:
{
"dswVersion": 2.2,
"dswRules": {
"secretPath": {
"match": {
"path": "\/private\/"
},
"apply": {
"redirect": "/not-allowed.html"
}
}
}
}
You can apply actions using variables from your regular expression, like this:
{
"dswVersion": 2.2,
"dswRules": {
"redirectWithVar": {
"match": {
"path": "\/old-site\/(.*)"
},
"apply": {
"redirect": "/redirected.html?from=$1"
}
}
}
}
Maybe you want to cache everything. Every single request (that is successful) will be cached as soon as it is loaded the first time:
{
"dswVersion": 2.2,
"dswRules": {
"cacheAll": {
"match": {
"path": "\/.*"
},
"apply": {
"cache": {
"name": "cached-files"
"version": 1
}
}
}
}
}
Most of times you will want to cache all your static files, like javascript files or css:
{
"dswVersion": 2.2,
"dswRules": {
"statics": {
"match": {
"extension": ["js", "css"]
},
"apply": {
"cache": {
"name": "page-static-files"
"version": 1
}
}
}
}
}
Some times you want to bypass some requests...this will cause DSW not to treat it even if it fails. When you use "request" for bypass value, it will execute the request and will not treat it. When you use "ignor" as the bypass value, it will not even request it...nothing will happen.
{
"dswVersion": 2.2,
"dswRules": {
"byPassable": {
"match": { "path": "/bypass/" },
"apply": {
"bypass": "request"
}
},
"ignorable": {
"match": { "path": "/ignore/" },
"apply": {
"bypass": "ignore"
}
}
}
}
In case you want to send credentials or other settings to fetch, you can use the options
property.
{
"dswVersion": 2.2,
"dswRules": {
"userData": {
"match": { "path": "\/api\/user\/.*" },
"options": { "credentials": "same-origin"},
"strategy": "online-first",
"apply": {
"indexedDB": {
"name": "userData",
"version": "3",
"key": "id",
"indexes": [
"name",
{
"name": "twitter",
"path": "twitter",
"options": {
"unique": true
}
}
]
}
}
}
}
You can also use it programatically, specially if you intend to use or create a tool to build, like grunt
or gulp
.
const options = {};
let dsw = requier('dsw');
dsw.generate('./path-to-project', options);
There is a client API as well, so you can use some features with aliases and shortcuts with the DSW client API.
You can enable notifications (the user will be asked to give permissions).
To do that, you can use the DSW.enableNotifications()
method, which will return a promise that resolves when the user enables it, and rejects if the user denies the permission.
DSW.enableNotifications().then(function(){
console.log('notification was shown');
}).catch(function(reason){
console.log("User hasn't allowed notifications", reason);
});
You can also show a notification using the DSW.notify
method.
This method will ask for permissions in case the user hasn't enabled it yet.
DSW.notify('The title', {
body: 'The message content',
icon: 'https://raw.githubusercontent.com/NascHQ/dsw/master/docs/images/worker-person.png',
duration: 5
}).then(function(){
console.log('notification was shown');
}).catch(function(reason){
console.log('Did not show the notification:', reason);
});
With DSW API you can listen to many events, including:
You can use both onpushnotification
or addEventListener for pushnotification
.
DSW.addEventListener('pushnotification', function(){
console.log('received it in addEventListener');
});
DSW.onpushnotification = function () {
console.log('received in onpushnotification');
}
Nowadays, notifications cannot carry any data, so you could use it to decide, in each client what to do with this information.
If you want to use it to actually show a notification (using webnotification), don't do it using this listener. In case your user has more tabs opened in your page, each one will trigger this event.
Instead, if you want to show some information, use the notification
configuration in your dswfile
.
You can use the methods DSW.online
and DSW.offline
to know if the device has internet connection*.
Also, you can use the method DSW.onNetworkStatusChange
to know WHEN the connection status changes.
DSW.onNetworkStatusChange(function(connected){
if (connected) {
console.log('Was offline and is now online');
} else {
console.log('Was online and is now offline');
}
});
Want to just see it working as fast as possible?
Clone the project, go to its directory, install it and run npm run try
So, you want to contribute? Cool! We need it! :)
We ask you to please read and follow our Code of Conduct.
Here is how...and yep, as Service workers are still a little too new, it is a little bit weird! Here is how I've been doing this, and if you have any better suggestion, please let me know :)
1 - Clone the project
git clone https://github.com/NascHQ/dsw
2 - Enter the project directory and install it
cd dsw
npm install
3 - Start watching it
npm run watch
4 - Use the sandbox to test it (run this command in another terminal window or tab, so the watch command can continue running)
npm run try
5 - Access in the browser, the address in the right port, as provided by the previous command, something like:
http://localhost:8888/
Please notice we use eslint
to validate the code styles. You can see the rules in the .eslintrc.js
file.
Whenever you change any files inside the src
directory, the watch will re-build it for you (wait until you see the "DONE" output).
This is automatic, but you stillneed to reload the try command in the other tab:
^C # ctrl+C to stop the previous try, and then...
npm run try
In the browser, though, you may face some boring situations, so, to make sure you will not fall into a trap debugging unchanged things, here goes some tips:
Go to the settings of your browser console and enable the "disable cache(when console is open)". This way, you will not be tricked by some unwanted caches.
Go to the "Application" tab in your console (in chrome, it is in canary by now) and:
1 - Click in "Service workers"
2 - Mark the box "Show All" (and when there is more than one, you may click in "Unregister")
3 - You can also check the box "Update on reload" to keep the latest service worker in command.
4 - When you want to test how things are working offline, simply check the "Offline" box.
5 - You can use the "Cache Storage" in the left panel to verify everything that has been cached.
6 - You can use the Lighthouse to validate the service worker situation: Lighthouse
If you have an idea or suggestion, please let us know by creating an issue at DSW Github Project page.
Service workers have been adopted by browsers and you can see an updated list here:
isServiceWorkerReady?
Some other projects that might help you too.
FAQs
Dynamic Service Worker, offline Progressive Web Apps much easier
The npm package dsw receives a total of 21 weekly downloads. As such, dsw popularity was classified as not popular.
We found that dsw demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.