Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sample Chrome extension for modifying set-cookie responses #3786

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/chrome-extension/manifest-v2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.yarn
dist.zip
.parcel-cache
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions examples/chrome-extension/manifest-v2/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "Sample Chrome Extension",
"version": "1.0.0",
"manifest_version": 2,
"description": "Sample Chrome Extension",
"homepage_url": "https://builder.io",
"icons": {
"16": "icons/builder-icon-small.png",
"48": "icons/builder-icon-medium.png",
"128": "icons/builder-icon-large.png"
},
"background": {
"scripts": [
"src/bg/background.js"
],
"persistent": true
},
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"permissions": [
"https://*/*",
"http://*/*",
"*://*/*",
"webRequest",
"webRequestBlocking",
"storage"
],
"externally_connectable": {
"matches": [
"*://*.builder.io/*",
"*://localhost:*/*"
]
}
}
44 changes: 44 additions & 0 deletions examples/chrome-extension/manifest-v2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@builder.io/sample-chrome-extension-manifest-v2",
"scripts": {
"version": "node tools/version.js",
"copy": "rm -rf ./dist && copyfiles ./icons/**/*.png ./icons/**/*.jpg ./src/bg/background.js ./manifest.json ./dist/modern",
"copy-final": "copyfiles -f 'copyfiles -f './dist/modern/*.js' ./dist/modern/src' ./dist/modern",
"build": "rm -rf .parcel-cache && npm run version && npm run copy",
"develop": "nodemon -e js,tsx,png,html,ts --watch src --exec \"NODE_ENV=development rm -rf .parcel-cache && npm run copy && yarn copy-final\"",
"dev": "yarn develop",
"parcel": "parcel",
"copyfiles": "copyfiles"
},
"engines": {
"node": "18",
"npm": "Please use yarn instead of NPM to install dependencies",
"yarn": ">=1.22.4 <2"
},
"devDependencies": {
"nodemon": "^2.0.4",
"typescript": "4.6.3"
},
"dependencies": {
"@parcel/transformer-typescript-tsc": "^2.13.0",
"@types/chrome": "^0.0.283",
"copyfiles": "^2.2.0",
"parcel": "^2.9.3",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"resolutions": {
"ip": "1.1.9"
},
"targets": {
"modern": {
"engines": {
"browsers": ">= 50%"
}
},
"default": {
"context": "browser",
"includeNodeModules": true
}
}
}
18 changes: 18 additions & 0 deletions examples/chrome-extension/manifest-v2/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Builder Chrome Extension

This is a sample chrome extension that is built for Manifest V2 of Chrome. It rewrites the `set-cookie` header to change the `SameSite` attribute whenever it is set to strict, to allow cookies to be set when it is rendered within the Builder Content Editor, which is an iFrame.

To be able to use this in production though, you need to change the `urls` in the `background.js` and `manifest.json` files to match your own domain. Furthermore, this extension will only work till June 2025, and will need an enterprise to enable Extension manifest V2 availability : https://chromeenterprise.google/policies/#ExtensionManifestV2Availability

Alternatively, use the Manifest V3 version of this extension : https://github.com/BuilderIO/builder/tree/main/examples/chrome-extension/manifest-v3

### To Develop

```
yarn
yarn build
```

Then navigate to `chrome://extensions/` in your browser. Toggle the setting in the top right called `Developer mode`. Then, click `Load unpacked` and select the `dist/modern` directory of this project (`/manifest-v2/dist`). You will then be able to load it reload it as you change the code.

Then, when it looks good, prepare a build using `yarn build`. The production code will be put into the `dist/modern` folder.
118 changes: 118 additions & 0 deletions examples/chrome-extension/manifest-v2/src/bg/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
console.debug('Background script loaded...');

const INITIATORS = [
'https://builder.io',
'https://qa.builder.io',
'https://beta.builder.io',
'https://local.builder.io',
'http://local.builder.io:1234',
'http://localhost:1234',

// TODO: Add your own domain here
'https://www.mydomain.com',
];

const REMOVE_HEADERS = ['x-frame-options', 'content-security-policy'];

let bound = false;

function addListeners() {
// FIXME: add raven to see what errors are occuring on first install

// TODO: move this to typescript to catch possible type issues like some of this data
// being optional and not existing
addEventListener('error', function (event) {
console.error('error', event.error, event.message, event.filename, event.lineno);
});

// TODO: limit requests this happens on to *.builder.io pages
// TODO: maybe chrome.webRequest isn't available until onInstall?
chrome.webRequest.onHeadersReceived.addListener(
function (details) {
if (
(details.type !== 'sub_frame' && details.frameType !== 'sub_frame') ||
!(
INITIATORS.includes(details.initiator) ||
details.url.includes('builder.editing=') ||
details.url.includes('csrf')
)
) {
return;
}

let modified = false;
const newResponseHeaders = details && details.responseHeaders || [];
newResponseHeaders?.forEach(header => {
if (header.name.toLowerCase() === 'set-cookie') {
if (header.value) {
// Cookies with SameSite=None must also specify Secure, meaning they require a secure context.
const sameSiteValue = ' SameSite=None';
// Cookies without a SameSite attribute will be treated as SameSite=Lax
const isLax =
header.value.toLowerCase().includes('samesite=lax') ||
!header.value.toLowerCase().includes('samesite');
if (isLax) {
let hasSamesite = false;
let hasSecure = false;
const parts = header.value.split(';').map(pair => {
if (pair.toLowerCase().includes('samesite')) {
hasSamesite = true;
return sameSiteValue;
}
if (pair.toLowerCase().includes('secure')) {
// we should add secure if not already there
hasSecure = true;
}
return pair;
});

if (!hasSamesite) {
parts.push(sameSiteValue);
}
if (!hasSecure) {
parts.push(' Secure');
}
header.value = parts.join(';');
modified = true;
}
}
}
});

if (modified) {
return {
responseHeaders: newResponseHeaders,
};
}
},
// Change this to your own domain
// { urls: ['<all_urls>'] },
{ urls: ['https://www.mydomain.com/*'] },
[
'blocking',
'responseHeaders',

// We need `extraHeaders` to be able to modify `x-frame-options` and
// `content-security-policy` headers.
// https://developer.chrome.com/docs/extensions/reference/webRequest/
'extraHeaders',
]
);

bound = true;
}

chrome.runtime.onInstalled.addListener(function () {
if (!bound) {
addListeners();
}
});

try {
if (!bound) {
addListeners();
}
} catch (error) {
console.error("Couldn't add listeners", error);
}

10 changes: 10 additions & 0 deletions examples/chrome-extension/manifest-v2/tools/version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const fs = require('fs');

const manifest = JSON.parse(fs.readFileSync('./manifest.json', 'utf8'));

const version = manifest.version.split('.').map(v => parseInt(v, 10));
version[2] += 1;

manifest.version = version.join('.');

fs.writeFileSync('./manifest.json', JSON.stringify(manifest, null, 2) + '\n');
Loading
Loading