Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -2739,6 +2739,77 @@ export class SenderTests extends AITestClass {
}
});

this.testCase({
name: 'Users could set the cross-origin header via request',
useFakeTimers: true,
test: () => {
let core = new AppInsightsCore();
let id = this._sender.identifier;
let coreConfig = {
instrumentationKey: 'abc',
isBeaconApiDisabled: true,
extensionConfig: {
[this._sender.identifier]: {
corsPolicy: "cross-origin",
}
}
}
core.initialize(coreConfig, [this._sender]);

let sendBeaconCalled = false;
this.hookSendBeacon((url: string) => {
sendBeaconCalled = true;
return true;
});

const telemetryItem: ITelemetryItem = {
name: 'fake item',
iKey: 'iKey',
baseType: 'some type',
baseData: {}
};

try {
this._sender.processTelemetry(telemetryItem);
this.clock.tick(30000);
} catch(e) {
QUnit.assert.ok(false);
}
const CrossOriginResourcePolicyHeader: string = "X-Set-Cross-Origin-Resource-Policy";
QUnit.assert.equal(1, this._getXhrRequests().length, "xhr sender is called");
let headers = this._getXhrRequests()[0].requestHeaders;
QUnit.assert.ok(headers.hasOwnProperty(CrossOriginResourcePolicyHeader));
QUnit.assert.equal(headers[CrossOriginResourcePolicyHeader], 'cross-origin');
QUnit.assert.notOk(this._getXhrRequests()[0].requestHeaders.hasOwnProperty('testHeader'));

// dynamic change
core.config.extensionConfig[this._sender.identifier].corsPolicy = "same-origin";
this.clock.tick(1);
try {
this._sender.processTelemetry(telemetryItem);
this.clock.tick(30000);
} catch(e) {
QUnit.assert.ok(false);
}
headers = this._getXhrRequests()[1].requestHeaders;
QUnit.assert.ok(headers.hasOwnProperty(CrossOriginResourcePolicyHeader));
QUnit.assert.equal(headers[CrossOriginResourcePolicyHeader], 'same-origin');
QUnit.assert.notOk(this._getXhrRequests()[1].requestHeaders.hasOwnProperty('testHeader'));

// dynamic change to null
core.config.extensionConfig[this._sender.identifier].corsPolicy = null;
this.clock.tick(1);
try {
this._sender.processTelemetry(telemetryItem);
this.clock.tick(30000);
} catch(e) {
QUnit.assert.ok(false);
}
headers = this._getXhrRequests()[2].requestHeaders;
QUnit.assert.notOk(this._getXhrRequests()[2].requestHeaders.hasOwnProperty(CrossOriginResourcePolicyHeader));
}
});

this.testCase({
name: 'Users are allowed to add customHeaders when endpointUrl is not Breeze.',
test: () => {
Expand Down
14 changes: 14 additions & 0 deletions channels/applicationinsights-channel-js/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,20 @@ export interface ISenderConfig {
* @since 3.2.0
*/
maxRetryCnt?: number;

/**
* [Optional] Specifies the Cross-Origin Resource Policy (CORP) for the endpoint.
* This value is included in the response header as `Cross-Origin-Resource-Policy`,
* which helps control how resources can be shared across different origins.
*
* Possible values:
* - `same-site`: Allows access only from the same site.
* - `same-origin`: Allows access only from the same origin (protocol, host, and port).
* - `cross-origin`: Allows access from any origin.
*
* @since 3.3.7
*/
corsPolicy?: string;
}

export interface IBackendResponse {
Expand Down
13 changes: 12 additions & 1 deletion channels/applicationinsights-channel-js/src/Sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,12 @@ const defaultAppInsightsChannelConfig: IConfigDefaults<ISenderConfig> = objDeepF
alwaysUseXhrOverride: cfgDfBoolean(),
transports: UNDEFINED_VALUE,
retryCodes: UNDEFINED_VALUE,
corsPolicy: UNDEFINED_VALUE,
maxRetryCnt: {isVal: isNumber, v:10}
});

const CrossOriginResourcePolicyHeader: string = "X-Set-Cross-Origin-Resource-Policy";

function _chkSampling(value: number) {
return !isNaN(value) && value > 0 && value <= 100;
}
Expand Down Expand Up @@ -268,7 +271,6 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControls {
let ctx = createProcessTelemetryContext(null, config, core);
// getExtCfg only finds undefined values from core
let senderConfig = ctx.getExtCfg(identifier, defaultAppInsightsChannelConfig);

let curExtUrl = senderConfig.endpointUrl;
// if it is not inital change (_endpointUrl has value)
// if current sender endpoint url is not changed directly
Expand All @@ -283,6 +285,15 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControls {
}
}

let corsPolicy = senderConfig.corsPolicy;
if (corsPolicy){
if (corsPolicy === "same-origin" || corsPolicy === "same-site" || corsPolicy === "cross-origin") {
this.addHeader(CrossOriginResourcePolicyHeader, corsPolicy);
}
} else {
delete _headers[CrossOriginResourcePolicyHeader];
}

if(isPromiseLike(senderConfig.instrumentationKey)) {
// if it is promise, means the endpoint url is from core.endpointurl
senderConfig.instrumentationKey = config.instrumentationKey as any;
Expand Down
Loading