Response headers update with declarativeNetRequest update rules not visible in Chrome's DevTools - google-chrome-extension

In my chrome extension V3 I use chrome.declarativeNetRequest.updateSessionRules to modify response headers:
chrome.declarativeNetRequest.updateSessionRules({
addRules: [
{
id: 999,
priority: 1,
condition: {
initiatorDomains: ['localhost'],
resourceTypes: ['main_frame']
},
action: {
type: 'modifyHeaders',
responseHeaders: [
{ header: 'Content-Security-Policy', operation: 'remove' },
{ header: "bar-foo-baz", operation: "set", value: "true" },
],
}
}]
});
The test I do is pretty simple. I violate the Content-Security-Policy rule so I get an error in the console. But when I apply the remove operation all works fine and the error is gone.
But the weird thing is if I look in the DevTools Network tab, I still see the Content-Security-Policy header and I don't see bar-foo-baz.
Does someone know why this is happening?

Related

Why does a rule added and immediately removed with declarativeNetRequest.updateDynamicRules not get removed even after the extension is reloaded?

I am trying to write an extension in Manifest Version 3, where I want to modify cookie headers for certain requests. Since the rule will only be applied to specific requests that meets my conditions,
I thought of adding a dynamic rule temporarily for that request, modify the cookie header, and immediately remove it. Here's the function for that rule.
if (condition) {
function makeNewRule(url) {
chrome.declarativeNetRequest.updateDynamicRules(
{
addRules:
[
{
"id": 1000,
"priority": 100,
"action": {
"type": "modifyHeaders",
"requestHeaders": [
{
"header": "cookie",
"operation": "set",
"value": "Modified cookie value 1"
}
]
},
"condition": {
"urlFilter" : url,
"resourceTypes":
["csp_report", "font", "image",
"main_frame", "media", "object",
"other", "ping", "script",
"stylesheet", "sub_frame",
"webbundle", "websocket",
"webtransport"]
}
}
],
removeRuleIds: [1000],
});
}
}
While this works for all requests that meet my condition, and the cookies are being modified observed in the chrome developers tool network window, the rule persists for a later session, even if I reload/update the unpacked extension. If I change the value of the cookie header to ""Modified cookie value 2", the developers tools still shows the previous "Modified cookie value 1". Therefore, I am assuming that the rule that I added is not being removed, and it is persisting across browser sessions. I tried cleaning the cache and reloading the browser. Additionally,
chrome.declarativeNetRequest.getDynamicRules(
e => console.log(e)
);
The snippet above shows the existence of the rule even when removed. How do I remove the rule that I added dynamically within that session?
I update my dynamic rules by removing and then adding them, I'll share my code with you, I hope it helps.
To update rules
function UpdateIntercept(token: string) {
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: GetInterceptRules(token).map((rule) => rule.id), // remove existing rules
addRules: GetInterceptRules(token)
});
chrome.declarativeNetRequest.getDynamicRules(
e => console.log(e)
);
}
then when I intercept the url
function GetInterceptRules(token: string) {
const allResourceTypes =
Object.values(chrome.declarativeNetRequest.ResourceType);
return [
{
id: 1,
priority: 1,
action: {
type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS,
requestHeaders: [
{
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
header: 'Authorization',
value: 'Bearer ' + token,
},
]
},
condition: {
urlFilter: liveApiUrl,
initiatorDomains: ["mail.google.com"],
resourceTypes: allResourceTypes,
}
},
{
id: 2,
priority: 1,
action: {
type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS,
requestHeaders: [
{
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
header: 'Authorization',
value: 'Bearer ' + token,
},
]
},
condition: {
urlFilter: testApiUrl,
initiatorDomains: ["mail.google.com"],
resourceTypes: allResourceTypes,
}
}
];
}
To intercept the url you want
function interceptURL(requestDetails: chrome.webRequest.WebRequestBodyDetails) {
console.log('intercepted: ' + requestDetails.url);
if (requestDetails.url.includes(liveApiUrl) || requestDetails.url.includes(testApiUrl)) { //maybe the inclue
chrome.runtime.sendMessage({ "message": "refresh_token" }, (token: string) => {
console.log('refreshed token: ' + token)
if (token == undefined) {
chrome.runtime.sendMessage({ "message": "get_token" });
}
});
}
}
I use onBeforeRequest
chrome.webRequest.onBeforeRequest.addListener(
interceptURL,
{ urls: [liveApiUrl, testApiUrl] }
)
In my case, I need to pass a refresh token everytime I intercept that url
still have a couple of things to work on, but hopefully it gives you some direction.

declarativeNetRequest.updateDynamicRules are not updating, how do I properly remove and add rules?

update: I noticed that the token is beinig passed on the first time I run itm, but no new tokens are being appended to my rules.
I have this chrome extension on Gmail where the user logs in and it return an access token, the token is passed to our API through HTTP Request, it works fine the first time around passing the access token, but if I don't refresh Gmail, after 1 hour the access token expires and I get 401 errors on my application. I have a function interceptURL that will match the url and give a refreshed token before the HTTP Request is made (or so I thought).
Maybe after 1h the access token is expired so calling refresh token wont generate a new token?
background script
function interceptURL(requestDetails: chrome.webRequest.WebRequestBodyDetails) {
console.log('intercepted: ' + requestDetails.url);
if (requestDetails.url.includes(liveApiUrl) || requestDetails.url.includes(testApiUrl)) {
chrome.runtime.sendMessage({ "message": "refresh_token" }, (token: string) => {
if (token == undefined) {
chrome.runtime.sendMessage({ "message": "get_token" });
}
});
}
}
chrome.webRequest.onBeforeRequest.addListener(
interceptURL,
{ urls: [liveApiUrl, testApiUrl] }
)
Here are my Rules
function GetInterceptRules(token: string) {
const allResourceTypes =
Object.values(chrome.declarativeNetRequest.ResourceType);
return [
{
id: 1,
priority: 1,
action: {
type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS,
requestHeaders: [
{
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
header: 'Authorization',
value: 'Bearer ' + token,
},
]
},
condition: {
urlFilter: liveApiUrl,
initiatorDomains: ["mail.google.com"],
resourceTypes: allResourceTypes,
}
},
{
id: 2,
priority: 1,
action: {
type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS,
requestHeaders: [
{
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
header: 'Authorization',
value: 'Bearer ' + token,
},
]
},
condition: {
urlFilter: testApiUrl,
initiatorDomains: ["mail.google.com"],
resourceTypes: allResourceTypes,
}
}
];
My thought was:
1 - I give it a refresh token before every HTTP Request so when I update the dynamic rules, it would pass the new token. (that's what I current have)
2 - I could check when the access token was created and just make sure the code to get token run before the 1 hour ends. (Maybe not the best approach?)
To get the access token
chrome.identity.launchWebAuthFlow(
{
url: azureTokenAuthUrl,
interactive: isInteractive
},
(redirectURL: string) => {
let token: string = '';
if (redirectURL != null) {
let params = new URLSearchParams(redirectURL);
token = params.get("access_token");
}
console.log("acces_token", token);
console.log(redirectURL)
UpdateIntercept(token)
callback(token)
}
Manifest V3
"permissions": [
"webRequest",
"declarativeNetRequest",
"declarativeNetRequestWithHostAccess",
"identity",
"identity.email"
],
"background": {
"service_worker": "/static/js/Background.js"
},
"content_scripts": [
{
"matches": [ "<all_urls>" ],
"css": [ "/css/bootstrap-iso.css" ],
"js": [ "react.production.min.js", "react-bootstrap.min.js", "react-dom.production.min.js" ]
},
{
"matches": [ "*://mail.google.com/*" ],
"css": [ "/css/AuthButton.css" ],
"js": [ "/static/js/AuthButton.js" ]
},
{
"matches": [ "*://mail.google.com/*" ],
"js": [ "/static/js/PushNotification.js" ]
}
],
I've been searching around but can't seem to find a solution for my problem.
I tried using JWT to decode so I know it's expired.
Added the launchAuthFlow with interactivity false to my listener where I check if an email has been opened, if so, trigger launchAuthFlow appending the token to the HTTP Request

Why are all sites blocked when I use declarativeNetRequest.updateDynamicRules

I need to block some http requests with new manifest v3. I try to use declarativeNetRequest.updateDynamicRules, but any rule blocks all requests, and I can't open any site. For example https://developer.chrome.com.
Code example:
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: [232],
addRules: [{
id: 232,
"condition": { "urlFilter": "analytics", "resourceTypes": ["main_frame"] },
action: {
"type": "block",
},
}]
});
Can somebody help me?)

Message extension sign-in not working properly

I have a message extension in the MS team, when I am trying to sign in it generates a pop-up with a validation code. I encountered this situation for the first time before that I just only need to enter the credentials.
const signInLink = await context.adapter.getSignInLink(context, this.connectionName);
return {
composeExtension: {
type: 'auth',
suggestedActions: {
actions: [{
type: 'openUrl',
value: signInLink,
title: 'MedxPlanner Authentication'
}]
}}
};
can you guide me where I am going wrong?
To implement authentication for messaging extension, you need to use below code snippet:
{
"composeExtension":{
"type":"auth",
"suggestedActions":{
"actions":[
{
"type": "openUrl",
"value": "{SignInLink}",
"title": "{Sign in to this app}"
}
]
}
}
}
Reference sample link:
https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/52.teams-messaging-extensions-search-auth-config

Returning XML in response from Loopback Remote Method

I am using the Loopback Connector REST (1.9.0) and have a remote method that returns XML:
Foo.remoteMethod
(
"getXml",
{
accepts: [
{arg: 'id', type: 'string', required: true }
],
http: {path: '/:id/content', "verb": 'get'},
returns: {"type": "string", root:true},
rest: {"after": setContentType("text/xml") }
}
)
The response always returns an escaped JSON string:
"<foo xmlns=\"bar\"/>"
instead of
<foo xmlns="bar"/>
Note that the response does have the content-type set to text/xml.
If I set my Accept: header to "text/xml", I always get "Not Acceptable" as a response.
If I set
"rest": {
"normalizeHttpPath": false,
"xml": true
}
In config.json, then I get a 500 error:
SyntaxError: Unexpected token <
I think that the "xml: true" property is simply causing a response parser to try to convert JSON into XML.
How can I get Loopback to return the XML in the response without parsing it? Is the problem that I am setting the return type to "string"? If so, what it the type that Loopback would recognize as XML?
You need to set toXML in your response object (more on that in a bit). First, set the return type to 'object' as shown below:
Foo.remoteMethod
(
"getXml",
{
accepts: [
{arg: 'id', type: 'string', required: true }
],
http: {path: '/:id/content', "verb": 'get'},
returns: {"type": "object", root:true},
rest: {"after": setContentType("text/xml") }
}
)
Next, you will need to return a JSON object with toXML as the only property. toXML should be a function that returns a string representation of your XML. If you set the Accept header to "text/xml" then the response should be XML. See below:
Foo.getXml = function(cb) {
cb(null, {
toXML: function() {
return '<something></something>';
}
});
};
You still need to enable XML in config.json because it's disabled by default:
"remoting": {
"rest": {
"normalizeHttpPath": false,
"xml": true
}
}
See https://github.com/strongloop/strong-remoting/blob/4b74a612d3c969d15e3dcc4a6d978aa0514cd9e6/lib/http-context.js#L393 for more info on how this works under the hood.
I recently ran into this with trying to create a dynamic response for Twilio voice calling in Loopback 3.x. In the model.remoteMethod call, explicitly specify the return type and content type as follows:
model.remoteMethod(
"voiceResponse", {
accepts: [{
arg: 'envelopeId',
type: 'number',
required: true
}],
http: {
path: '/speak/:envelopeId',
"verb": 'post'
},
returns: [{
arg: 'body',
type: 'file',
root: true
}, {
arg: 'Content-Type',
type: 'string',
http: {
target: 'header'
}
}],
}
)
Have your method return both the xml and the content type via the callback function. Note that the first argument in the callback is to return an error if one exists:
model.voiceResponse = function(messageEnvelopeId, callback) {
app.models.messageEnvelope.findById(messageEnvelopeId, {
include: ['messages']
}).then(function(res) {
let voiceResponse = new VoiceResponse();
voiceResponse.say({
voice: 'woman',
language: 'en'
},
"This is a notification alert from your grow system. " + res.toJSON().messages.message
)
callback(null,
voiceResponse.toString(), // returns xml document as a string
'text/xml'
);
});
}

Resources