cssRules/rules are null in Chrome - css

My chrome extension needs to modify certain css rules on user's page. Accessing styles via document.styleSheets only gives access to styles linked from within the same domain. Other elements of document.styleSheets array have cssRules/rules set to null.
Why is it cross domain policy applies here? Styles are being applied anyway regardless of their origin, so what is the point? And how to get around it in my case?
EDIT:
The reason I need to MODIFY user css rules (as opposed to simply adding my own) is that I need to protect custom element injected by extension from being affected by * rules. see details in this question

Content scripts don't have any cross-domain privileges comparing to a regular javascript, so any limitations are carried over. See related question #1, question #2.
You can inject your own css style in the manifest:
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"]
}
]
where you can try to overwrite original styles by defining rules with higher specificity.
You can also just tweak concrete element styles through javascript:
document.getElementById("id").style.property="value";

I fixed my version of the issue by changing the url from http:// to https://. Doh!

First, content scripts can't access cross-origin scripts.
Second, you can fetch cross-origin scripts from the background service worker (MV3) in a Chrome Extension.
What I am doing in my Chrome Extension's content script is using this to iterate over the stylesheets and sending the failed stylesheet link to the background script in the catch statement
let styleSheets = [];
// Iterating over all stylesheets
[...document.styleSheets].map(function(styleSheet) {
try {
[...styleSheet.cssRules].map(function(cssRule) {
styleSheets.push(cssRule);
});
} catch (e) {
styleSheets.push(null);
// Send failed stylesheets to service worker from here
}
});
And in the background server worker, using this function to get the stylesheet.
fetch(styleSheetUrl)
.then((response) => {
if (response.status >= 200 && response.status < 300) return response.text();
else return false;
})
.then((styleSheet) => {
console.log(styleSheet);
});

Related

Loading local file inside an iframe in Electron

I've tried different approaches but all are problematic.
So first of all I was using webview, but as per electron documentation, this tag is undergoing major architectural changes and it's recommended to use iframe or other alternatives. Furthermore, the webview tag gives me a warning while used alongside VueJS that the component is not registered. I understand this component doesn't exist within HTML standards and is something specific to electron, so I am not sure how to tell Vue to ignore or recognize it in the use case of an electron app.
Coming to the iframe problem, approach one of loading the file directly via src, gives me the obvious error Not allowed to load local resource:. Turning off webSecurity though allows the file to load but I read it's not recommended to turn it off. I am not sure if there are specific use case where it's safe to turn it off or shouldn't be at all.
I decided to try via file protocol as I already have it in place. The protocol code:
protocol.registerFileProtocol('downloads', (request, callback) => {
const url = request.url.substring('downloads:///'.length)
const location = path.normalize(paths.downloads(url))
callback({ path: location })
})
Though when I load the file this way, the renderer process crash without errors. Is there something in addition to the above which would help loading local files via iframe?
Edit 1
My use case is the following: I have a typical entry point to an index.html which contains code for a VueJS app.
if (app.isPackaged) {
window.loadFile(join(__dirname, '../renderer/index.html'))
} else {
// 🚧 Use ['ENV_NAME'] avoid vite:define plugin
const url = `http://${process.env['VITE_DEV_SERVER_HOST']}:${process.env['VITE_DEV_SERVER_PORT']}`
window.loadURL(url)
window.webContents.openDevTools()
}
Inside that VueJS app, I require to list html files from a directory. I am able to achieve so via webview but I have tried to move away from it for the reason mentioned above. I tried using iframe but encountered issues as well. If there's a setting that doesn't turn off all security and allows me to load the file via iframe, that would be ideal.
This is kind of the reverse of this question where they're using an iframe, running into the "not allowed to load local resource" and being told to use a <webview> instead.
The <webview> docs list BrowserView as another alternative which is what I would recommend here. That should be much easier to work with than an iframe.
const { app, BrowserView, BrowserWindow } = require('electron')
app.whenReady().then(() => {
const win = new BrowserWindow()
const view = new BrowserView()
win.setBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 300, height: 300 })
view.webContents.loadFile('<yourFile>')
})
Even though it is not recommended to use webview tag, I decided to go forward as it's the only thing that works for me. The only issue then was this error where Vue does not recognize the tag. To work around that error/warning, I had to update my vite.js config:
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag === 'webview'
}
}
}),
// ...

How to enable the use of <a> tag in Next.js?

I don't want to use <Link> from next. I've been researching how to disable this, but I can't.
I want to disable this and use the default html tag <a>.
It doesn't let me deploy, it keeps giving error because of that.
Can anyone help me?
The error I get:
12:9 Error: Do not use the HTML tag to navigate to /. Use Link
from 'next/link' instead. See:
https://nextjs.org/docs/messages/no-html-link-for-pages
#next/next/no-html-link-for-pages
You can disable the #next/next/no-html-link-for-pages ESLint rule in your .eslintrc file.
{
// ...
"rules": {
// Other rules
"#next/next/no-html-link-for-pages": "off"
}
}
Note that the above is not recommended, as using <a> elements instead of the next/link component will disable client-side route transitions between pages. This means all pages will be loaded from the server.
I guess you're trying to navigate to home. You can try:
Home
Instead of:
Home

NextJS Content Security Policy (CSP)

I am currently making a Content Security Policy (CSP) for a production application made with Next.js. While I have found trustworthy documentation for implementing a CSP with the framework, there are a couple of concerns that I want to make sure are addressed correctly.
Issue #1: I have read that security policies set in HTTP headers are preferable. However, I cannot find a way to pass a 'nonce' attribute for inline styles in production using this approach. https://nextjs.org/docs/advanced-features/security-headers
Issue #2: I've seen other examples where developers inject their CSP in the custom document("./pages/_document.js"). I am hesitant to use this approach because I hear meta-tag CSPs are easily bypassable. https://github.com/vercel/next.js/tree/canary/examples/with-strict-csp
My Questions:
Is there a way to use a 'nonce' with header configuration in "next.config.js"? If so, how?
Is specifying "unsafe-inline" for styles in production a security issue at all if Next.js automatically sanitizes user input? I should also mention that I sanitize all mongo database queries in my APIs as well.
Is there something about the meta tag approach described in Issue #2 that makes it as secure as the HTTP header approach?
What approach do you recommend I take to make my CSP as strong as possible for my web app?
All the best,
-Sam
NextJS has 2 pre-rendering mode: Static Site Generation(SSG) and Server-Side Rendering(SSR). The first one has no way to update nonce='value' in the HTML code, but when SSG you can pass a 'nonce' attribute for inline styles and scripts using ./pages/_document.tsx.
See an example for CSP header and meta tag CSP example.
Re Quersions:
I think using a next.config.js is possible, for example next-safe package adds a nextSafe() function into this file to set a lot of headers:
.
const nextSafe = require('next-safe')
const isDev = process.env.NODE_ENV !== 'production'
module.exports = {
async headers () {
return [
{
source: '/:path*',
headers: nextSafe({ isDev }),
},
]
},
}
To set 'nonce' into CSP header you can craft your own function in this way. To set 'nonce' attribute into styles you can use _document.tsx renderer.
Specifying 'unsafe-inline' for styles in production is not a security issue. For example https://accounts.google.com page allows inline styles (it even does not have style-src/default-src directives but it carefully controls scripts).
Setting CSP in HTTP headers is preferable but this does not mean that CSP in meta tag is easily bypassable. Just CSP in meta tag has some restrictions and if you do not use features that have been restricted you can safely use meta tag to delivery CSP.
You can strengthen the protection indefinitely, spending a lot of time and resources. Just follow the main principle "protection should not be more expensive than the protected object".

Electron: Security concern regarding inline styles in an iframe

Question: I'm trying to load an external script (let's say from example.com) in the Electron app. The External script loads everything in an iframe and it's using inline styles, which renders the widget without any styles due to Electron CSP. I don't want to use unsafe-inline CSP on the whole application, I need it just on this iframe which comes from "example.com".
Error which I get: Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'self'"
For resolving this I am thinking to add this code block in my electron app -
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
if(details.url.includes("example.com") && details.url.includes(".html")) {
callback({
responseHeaders: Object.assign({
"Content-Security-Policy": [ "default-src 'self';style-src 'unsafe-inline' https://*.example.com https://example.com" ]
}, details.responseHeaders)});
}
This code block will ensure to have 'unsafe-inline' CSP only in the iframes of "abc.com". But, I want to check that can this code block still cause any kind of security threat to my app?
Does your iframe contain any user entered data? If not then this is fine. If it does then you will need to find another way to check they have not entered any inline styles

How can I apply GA download tracking with Sitecore?

I'm posting this question to Stackflow b/c after doing much research into an answer to this very question online, I did not come across a straight forward answer and had to do my own sleuthwork to resolve this.
Basically, Sitecore uses a handler file .ASHX for all files uploaded to the Media Library. Since the 3rd party GA tracking tool I was using (entourage.js or gatags.js) does not recognize .ashx as a whitelisted download file, it was not adding the appropriate GA tracking syntax to the GA pixel tracker (__utm.gif).
So the solution turns out to be simple but sadly, not retroactive, meaning all files previously uploaded to the Media Library in the Sitecore content tree will continue to use the ashx extension unless you reupload the image. In your web.config file, search for the "Media.RequestExtension" setting. If you change the value associated with this setting from "ashx" to a blank string, this will force Sitecore to use the originalextension of the file and image in the Sitecore Media Library.
Aside from interfering with GA analytics, this method of turning every downloadable file extension into an ashx file is poor SEO practice. AND, Sitecore will not point you in the right direction of getting around this other than a round-about way (google Sitecore dynamic linking and configuration) because they want you to use their Sitecore OMS download tracking capability. And that's it! Two days of research led me to this conclusion.
So the solution turns out to be simple but sadly, not retroactive,
meaning all files previously uploaded to the Media Library in the
Sitecore content tree will continue to use the ashx extension unless
you reupload the image.
Not sure where you got this information, but it's incorrect. You can blank out the Media.RequestExtension setting and all existing files will use their original extension. In IIS7 Integrated Mode, you should be able to make this change without having to make other server configuration changes.
Edit: More Info
If you analyze Sitecore.Configuration.Settings.Media.RequestExtension (the API equivalent to this settings) in a decompiler, you can see that it's only used by the MediaProvider when constructing the Media URL. Sitecore should remember the original extension of the media and can serve it with its original URL, regardless of what this setting was when it was uploaded. That's my experience, anyway, and it seems to be validated by looking into Sitecore.Kernel.
You could use this script to track download events via Google Analytics.
if (typeof jQuery != 'undefined') {
jQuery(document).ready(function($) {
var filetypes = /\.(zip|pdf|doc*|xls*|ppt*|jpg|ashx)$/i;
var baseHref = '';
if (jQuery('base').attr('href') != undefined) baseHref = jQuery('base').attr('href');
jQuery('a').each(function() {
var href = jQuery(this).attr('href');
if (href) {
if (href.indexOf('?') != '-1') {
href = href.substring(0, href.indexOf('?'));
}
if (href.match(filetypes)) {
jQuery(this).click(function() {
var extension = String((/[.]/.exec(href)) ? /[^.]+$/.exec(href) : undefined);
var filePath = String(href);
_gaq.push(['_trackEvent', 'Download', extension, filePath]);
if (jQuery(this).attr('target') != undefined && jQuery(this).attr('target').toLowerCase() != '_blank') {
setTimeout(function() {
location.href = baseHref + href;
}, 200);
return false;
}
});
}
}
});
});
}
Just add in the required file types here at this line -
var filetypes = /.(zip|pdf|doc*|xls*|ppt*|jpg|ashx)$/i;
Having done a quick google for gatags.js, I can see that you can add an extension to the whitelist on line 24:
var isDoc = path.match(/\.(?:doc|eps|jpg|png|svg|xls|ppt|pdf|xls|zip|txt|vsd|vxd|js|css|rar|exe|wma|mov|avi|wmv|mp3)($|\&|\?)/);
Change it to:
var isDoc = path.match(/\.(?:ashx|doc|eps|jpg|png|svg|xls|ppt|pdf|xls|zip|txt|vsd|vxd|js|css|rar|exe|wma|mov|avi|wmv|mp3)($|\&|\?)/);
Alternatively, you could attach the Google Analytics _trackEvent yourself with a dom selector and a click event.
Either way, I think OMS can track media library files regardless of extension - removing the default ashx extension doesn't stop the file being handled by Sitecore.

Resources