What is a web page resources tracker?
The web page resources tracker is a utility that gives developers the ability to detect and track resources of any web page. It falls under the category of synthetic monitoring tools and helps ensure that the deployed application loads only the intended web resources (JavaScript and CSS) during its lifetime. If any unintended changes occur, which could result from a broken deployment or malicious activity, the tracker will promptly notify developers or IT personnel about the detected anomalies.
Additionally, security researchers focused on discovering potential security vulnerabilities in third-party web applications can use web page resources trackers. By being notified when the application's resources change, researchers can identify if the application has been upgraded, providing an opportunity to re-examine the application and potentially discover new vulnerabilities.
Currently, Secutils.dev doesn't support tracking resources for web pages protected by application firewalls (WAF) or any form of CAPTCHA. If you require tracking resources for such pages, please comment on #secutils/34 to discuss your use case.
On this page, you can find guides on creating and using web page resources trackers.
Create a web page resources tracker
In this guide, you'll create a simple resources tracker for the Hacker News:
- Navigate to Web Scraping → Resources trackers and click Track resources button
- Configure a new tracker with the following values:
Name |
|
URL |
|
- Click the Save button to save the tracker
- Once the tracker is set up, it will appear in the trackers grid
- Expand the tracker's row and click the Update button to make the first snapshot of the web page resources
It's hard to believe, but as of the time of writing, Hacker News continues to rely on just a single script and stylesheet!
Watch the video demo below to see all the steps mentioned earlier in action:
Detect changes with a web page resources tracker
In this guide, you will create a web page resources tracker and test it using a custom HTML responder:
- First, navigate to Webhooks → Responders and click Create responder button
- Configure a few responders with the following values to emulate JavaScript files that we will track changes for across revisions:
This JavaScript will remain unchanged across revisions:
Name |
|
Path |
|
Headers |
|
Body |
|
This JavaScript will change across revisions:
Name |
|
Path |
|
Headers |
|
Body |
|
This JavaScript will be removed across revisions:
Name |
|
Path |
|
Headers |
|
Body |
|
This JavaScript will be added in a new revision:
Name |
|
Path |
|
Headers |
|
Body |
|
- Now, configure a new responder with the following values to respond with a simple HTML page that references previously created JavaScript responders (except for
added.js
):
Name |
|
Path |
|
Headers |
|
Body |
|
- Click the Save button to save the responder
- Once the responder is set up, it will appear in the responders grid along with its unique URL
- Click on the responder's URL and make sure that it renders the following content:
Source: no-changes.js
Source: changed.js, Changed: no
Source: removed.js
- Now, navigate to Web Scraping → Resources trackers and click Track resources button
- Configure a new tracker for
track-me.html
responder with the following values:
Name |
|
URL |
|
Frequency |
|
Notifications |
|
Configured tracker will fetch the resources of the track-me.html
responder once a day and notify you if any changes are detected. You can change the frequency and notification settings to suit your needs.
- Click the Save button to save the tracker
- Once the tracker is set up, it will appear in the trackers grid
- Expand the tracker's row and click the Update button to make the first snapshot of the web page resources
- Once the tracker has fetched the resources, they will appear in the resources grid:
Source | Diff | Type | Size |
---|---|---|---|
https://[YOUR UNIQUE ID].webhooks.secutils.dev/no-change.js | - | Script | 81 |
https://[YOUR UNIQUE ID].webhooks.secutils.dev/changed.js | - | Script | 91 |
https://[YOUR UNIQUE ID].webhooks.secutils.dev/removed.js | - | Script | 78 |
- Now, navigate to Webhooks → Responders and edit
track-me.html
responder to referenceadded.js
responder, and remove reference toremoved.js
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Evaluate resources tracker</title>
<script type="text/javascript" src="./no-changes.js" defer></script>
<script type="text/javascript" src="./changed.js" defer></script>
- <script type="text/javascript" src="./removed.js" defer></script>
+ <script type="text/javascript" src="./added.js" defer></script>
</head>
<body></body>
</html>
- Next, change the body of the
changed.js
responder to something like this:
document.body.insertAdjacentHTML(
'beforeend',
- 'Source: changed.js, Changed: no<br>'
+ 'Source: changed.js, Changed: yes<br>'
);
- Finally, navigate to Web Scraping → Resources trackers and expand the
Demo
tracker's row - Click Update button to fetch the next revision of the web page resources
Normally, Secutils.dev caches web page resources for 10 minutes. This means that if you make changes to the web page resources and want to see them reflected in the tracker, you'll need to wait for 10 minutes before re-fetching resources. However, for this guide, I've disabled caching for the tracker so that you can see changes immediately.
- Once the tracker has fetched updated resources, they will appear in the resources grid together with the diff status:
Source | Diff | Type | Size |
---|---|---|---|
https://[YOUR UNIQUE ID].webhooks.secutils.dev/no-change.js | - | Script | 81 |
https://[YOUR UNIQUE ID].webhooks.secutils.dev/changed.js | Changed | Script | 91 |
https://[YOUR UNIQUE ID].webhooks.secutils.dev/added.js | Added | Script | 76 |
https://[YOUR UNIQUE ID].webhooks.secutils.dev/removed.js | Removed | Script | 78 |
Watch the video demo below to see all the steps mentioned earlier in action:
Filter resources with a web page resources tracker
In this guide, you will create a web page resource tracker for the Reddit home page and learn how to track only specific resources:
- Navigate to Web Scraping → Resources trackers and click Track resources button
- Configure a new tracker with the following values:
Name |
|
URL |
|
Normally, Secutils.dev caches web page resources for 10 minutes. This means that if you make changes to the web page resource tracker and want to see them take effect, you'll need to wait for 10 minutes before re-fetching resources. However, for this guide, I'm adding an arbitrary ?rev=X
query string parameter to the URL to bypass caching and see the changes immediately. This trick can be quite handy when you are setting up a new tracker and need to fine-tune its configuration.
Note that every time you change the tracker's URL, all previously fetched resources will be removed.
- Click the Save button to save the tracker
- Once the tracker is set up, it will appear in the trackers grid
- Expand the tracker's row and click the Update button to make the first snapshot of the web page resources
- Once the tracker has fetched the resources, they will appear in the resources grid. You'll notice that there are nearly 80 resources used for the GitHub home page! In the case of large and complex pages like this one, it's recommended to have multiple separate trackers, e.g. one per logical functionality domain, to avoid overwhelming the developer with too many resources and consequently changes they might need to track. Let's say we're only interested in "vendored" resources.
- To filter out all resources that are not "vendored", we'll use the
Resource filter/mapper
feature. Click the pencil icon next to the tracker's name to edit the tracker and update the following properties:
URL |
|
Resource filter/mapper |
|
- The Resource filter/mapper property accepts a JavaScript function that is executed for each resource detected by the tracker. The function receives a single
resource
argument, which is the resource object. The function must return either the resource object ornull
. If the function returnsnull
, the resource will be filtered out and will not be tracked. In our case, we're filtering out all resources that do not contain sso in their URL. You can learn more about resource filter/mapper scripts in the Annex: Resource filter/mapper script examples section. - Now, click the Save button to save the tracker.
- Click the Update button to re-fetch web page resources. Once the tracker has re-fetched resources, only about half of the previously extracted resources will appear in the resources grid.
Watch the video demo below to see all the steps mentioned earlier in action:
Annex: Resource filter/mapper script examples
In this section, you can find examples of resource filter and mapper scripts that you can use to filter out or map resources based on various criteria. The script essentially defines a function that is executed for each resource detected by the tracker and receives a single resource
argument, which is the resource object. The function must return either the resource object or null
. If the function returns null
, the resource will be filtered out and will not be tracked.
The resource
argument has the following interface:
interface Resource {
// Resource full URL. This property is not defined for inline resources.
url?: string;
// Resource content.
data: string;
// Resource type.
type: 'script' | 'stylesheet';
}
Track only external resources
return resource.url?.startsWith('http')
? resource
: null;
Track only inline resources
return !resource.url
? resource
: null;
Track only JavaScript resources
return resource.type === 'script'
? resource
: null;
Track only CSS resources
return resource.type === 'stylesheet'
? resource
: null;
Strip query string parameters from resource URLs
Sometimes, resources such as analytics and user tracking scripts load with unique query string parameters, even when the content of the resource remains constant. This can lead to confusion for the tracker and trigger unwanted change notifications. To address this, you can strip query string parameters from the URLs of such resources before calculating the resource fingerprint:
const isInlineResource = !resource.url;
if (isInlineResource) {
return resource
}
const isGoogleAnalyticsResource = resource.url.includes('googletagmanager');
if (!isGoogleAnalyticsResource) {
return resource
}
// Strip query string parameters from Google Analytics resource URLs.
const [urlWithoutQueryString] = resource.url.split('?');
return { ...resource, url: urlWithoutQueryString };
Annex: Custom cron schedules
Custom cron schedules are available only for Pro subscription users.
In this section, you can learn more about the supported cron expression syntax used to configure custom tracking schedules. A cron expression is a string consisting of six or seven subexpressions that describe individual details of the schedule. These subexpressions, separated by white space, can contain any of the allowed values with various combinations of the allowed characters for that subexpression:
Subexpression | Mandatory | Allowed values | Allowed special characters |
---|---|---|---|
Seconds | Yes | 0-59 | * / , - |
Minutes | Yes | 0-59 | * / , - |
Hours | Yes | 0-23 | * / , - |
Day of month | Yes | 1-31 | * / , - ? |
Month | Yes | 0-11 or JAN-DEC | * / , - |
Day of week | Yes | 1-7 or SUN-SAT | * / , - ? |
Year | No | 1970-2099 | * / , - |
Following the described cron syntax, you can create almost any schedule you want as long as the interval between two consecutive checks is longer than 10 minutes. Below are some examples of supported cron expressions:
Expression | Meaning |
---|---|
0 0 12 * * ? | Run at 12:00 (noon) every day |
0 15 10 ? * * | Run at 10:15 every day |
0 15 10 * * ? | Run at 10:15 every day |
0 15 10 * * ? * | Run at 10:15 every day |
0 15 10 * * ? 2025 | Run at 10:15 every day during the year 2025 |
0 0/10 14 * * ? | Run every 10 minutes from 14:00 to 14:59, every day |
0 10,44 14 ? 3 WED | Run at 14:10 and at 14:44 every Wednesday in March |
0 15 10 ? * MON-FRI | Run at 10:15 from Monday to Friday |
0 11 15 8 10 ? | Run every October 8 at 15:11 |
To assist you in creating custom cron schedules, Secutils.dev lists five upcoming scheduled times for the specified schedule: