Skip to content

Commit

Permalink
feat(browser-extension): Improve support for custom domains for vario…
Browse files Browse the repository at this point in the history
…us integration
  • Loading branch information
Darran Boyd committed Nov 25, 2024
1 parent e92aad4 commit f37a9a0
Show file tree
Hide file tree
Showing 8 changed files with 386 additions and 159 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
limitations under the License.
******************************************************************************************************************** */

import { getExtensionConfig, TCConfig } from './popup/config';
import { getExtensionConfig, TCConfig, IntegrationTypes } from './popup/config';
import { logDebugMessage } from '../debugLogger';

const tcButtonText = 'View in Threat Composer';
Expand Down Expand Up @@ -93,6 +93,7 @@ async function getTCJSONCandidate(url: string, element: HTMLElement, config: TCC
}
})
.catch(function (error) {
console.log(error);
logDebugMessage(config, 'Error during fetch: ' + error.message);
});
};
Expand All @@ -113,25 +114,12 @@ async function handleRawFile(config: TCConfig) {
};

function isRawSite(tcConfig: TCConfig) {
if (matchesAnyRegex(window.location.href, tcConfig.integrationRawUrlRegexes)) {return true;}
if (matchesAnyRegex(window.location.href, tcConfig.integrations[IntegrationTypes.RAW].urlRegexes)) {return true;}
return false;
}

function isGitLabSite(tcConfig: TCConfig) {
if (matchesAnyRegex(window.location.href, tcConfig.integrationGitLabCodeBrowserUrlRegexes)) {return true;}

// Check for the presence of the GitLab logo or branding
const gitlabLogo = document.querySelector('img[src*="gitlab.com/assets/logo"]');
if (gitlabLogo) {
return true;
}

// Check for the presence of specific GitLab UI elements
const gitlabLayout = document.querySelector('.layout-page');
const gitlabHeader = document.querySelector('.navbar-gitlab');
if (gitlabLayout && gitlabHeader) {
return true;
}
if (matchesAnyRegex(window.location.href, tcConfig.integrations[IntegrationTypes.GITLAB].urlRegexes)) {return true;}

// Check for the presence of GitLab-specific classes or IDs
const gitlabElements = document.querySelectorAll('[class*="gl-"], [id*="gl-"]');
Expand All @@ -144,26 +132,26 @@ function isGitLabSite(tcConfig: TCConfig) {
}

function isGitHubSite(tcConfig: TCConfig) {
if (matchesAnyRegex(window.location.href, tcConfig.integrationGitHubCodeBrowserUrlRegexes)) {return true;}
if (matchesAnyRegex(window.location.href, tcConfig.integrations[IntegrationTypes.GITHUB].urlRegexes)) {return true;}
return false;
}

function isCodeCatalystSite(tcConfig: TCConfig) {
if (matchesAnyRegex(window.location.href, tcConfig.integrationCodeCatalystCodeBrowserUrlRegexes)) {return true;}
if (matchesAnyRegex(window.location.href, tcConfig.integrations[IntegrationTypes.CODECATALYST].urlRegexes)) {return true;}
return false;
}

function isAmazonCodeBrowser(tcConfig: TCConfig) {
if (matchesAnyRegex(window.location.href, tcConfig.integrationAmazonCodeBrowserUrlRegexes)) {return true;}
if (matchesAnyRegex(window.location.href, tcConfig.integrations[IntegrationTypes.CODEAMAZON].urlRegexes)) {return true;}
return false;
}

function isBitbucketSite(tcConfig: TCConfig) {
if (matchesAnyRegex(window.location.href, tcConfig.integrationBitBucketCodeBrowserUrlRegexes)) {return true;}
if (matchesAnyRegex(window.location.href, tcConfig.integrations[IntegrationTypes.BITBUCKET].urlRegexes)) {return true;}

// Check for the presence of Bitbucket-specific elements
const bitbucketMeta = document.querySelectorAll('meta[name="application-name"][content="Bitbucket"]');
if (bitbucketMeta) {
if (bitbucketMeta.length > 0) {
return true;
}

Expand Down Expand Up @@ -356,38 +344,38 @@ async function handleCodeCatalystCodeViewer(codeCatalystState: TCCodeCatalystSta
async function ContentScriptInScope(tcConfig: TCConfig) {
let inScopeRegexes = [tcConfig.baseUrlRegex];

if (tcConfig.integrationRaw) {
tcConfig.integrationRawUrlRegexes.forEach(entry => {
if (tcConfig.integrations[IntegrationTypes.RAW].enabled) {
tcConfig.integrations[IntegrationTypes.RAW].urlRegexes.forEach(entry => {
inScopeRegexes.push(entry);
});
}

if (tcConfig.integrationAmazonCodeBrowser) {
tcConfig.integrationAmazonCodeBrowserUrlRegexes.forEach(entry => {
if (tcConfig.integrations[IntegrationTypes.CODEAMAZON].enabled) {
tcConfig.integrations[IntegrationTypes.CODEAMAZON].urlRegexes.forEach(entry => {
inScopeRegexes.push(entry);
});
}

if (tcConfig.integrationBitBucketCodeBrowser) {
tcConfig.integrationBitBucketCodeBrowserUrlRegexes.forEach(entry => {
if (tcConfig.integrations[IntegrationTypes.BITBUCKET].enabled) {
tcConfig.integrations[IntegrationTypes.BITBUCKET].urlRegexes.forEach(entry => {
inScopeRegexes.push(entry);
});
}

if (tcConfig.integrationCodeCatalystCodeBrowser) {
tcConfig.integrationCodeCatalystCodeBrowserUrlRegexes.forEach(entry => {
if (tcConfig.integrations[IntegrationTypes.CODECATALYST].enabled) {
tcConfig.integrations[IntegrationTypes.CODECATALYST].urlRegexes.forEach(entry => {
inScopeRegexes.push(entry);
});
}

if (tcConfig.integrationGitHubCodeBrowser) {
tcConfig.integrationGitHubCodeBrowserUrlRegexes.forEach(entry => {
if (tcConfig.integrations[IntegrationTypes.GITHUB].enabled) {
tcConfig.integrations[IntegrationTypes.GITHUB].urlRegexes.forEach(entry => {
inScopeRegexes.push(entry);
});
}

if (tcConfig.integrationGitLabCodeBrowser) {
tcConfig.integrationGitLabCodeBrowserUrlRegexes.forEach(entry => {
if (tcConfig.integrations[IntegrationTypes.GITLAB].enabled) {
tcConfig.integrations[IntegrationTypes.GITLAB].urlRegexes.forEach(entry => {
inScopeRegexes.push(entry);
});
}
Expand All @@ -403,8 +391,8 @@ async function ContentScriptInScope(tcConfig: TCConfig) {
return match;
}

function matchesAnyRegex(url: string, regexArray: RegExp[]) {
return regexArray.some(regex => regex.test(url));
function matchesAnyRegex(url: string, regexArray: string[]) {
return regexArray.some(regex => new RegExp(regex).test(url));
}

export default defineContentScript({
Expand Down Expand Up @@ -448,18 +436,18 @@ export default defineContentScript({
}

if (
tcConfig.integrationRaw && isRawSite(tcConfig)) {
tcConfig.integrations[IntegrationTypes.RAW].enabled && isRawSite(tcConfig)) {
logDebugMessage(tcConfig, 'Assuming raw file view');
await handleRawFile(tcConfig);
} else if (tcConfig.integrationGitLabCodeBrowser && isGitLabSite(tcConfig)) {
} else if (tcConfig.integrations[IntegrationTypes.GITLAB].enabled && isGitLabSite(tcConfig)) {
logDebugMessage(tcConfig, 'Assuming GitLab code browser');
await handleGitLabBrowser(gitLabState, tcConfig);
let observerForGitLabCodeBrowser = new MutationObserver(
() => handleGitLabBrowser(gitLabState, tcConfig),
);
observerForGitLabCodeBrowser.observe(document, config); //Scope is `document` as GitLab is a SPA
} else if (
tcConfig.integrationGitHubCodeBrowser && isGitHubSite(tcConfig)
tcConfig.integrations[IntegrationTypes.GITHUB].enabled && isGitHubSite(tcConfig)
) {
logDebugMessage(tcConfig,
'Assuming GitHub code browser',
Expand All @@ -470,7 +458,7 @@ export default defineContentScript({
);
observerForGitHubCodeViewer.observe(document, config); //Scope is `document` as GitHub is a SPA
} else if (
tcConfig.integrationCodeCatalystCodeBrowser && isCodeCatalystSite(tcConfig)
tcConfig.integrations[IntegrationTypes.CODECATALYST].enabled && isCodeCatalystSite(tcConfig)
) {
logDebugMessage(tcConfig, 'Assuming Code Catalyst code browser');
//Inject script
Expand All @@ -485,7 +473,7 @@ export default defineContentScript({
);
observerForCodeCatalystCodeViewer.observe(document.body, config);
} else if (
tcConfig.integrationAmazonCodeBrowser && isAmazonCodeBrowser(tcConfig)
tcConfig.integrations[IntegrationTypes.CODEAMAZON].enabled && isAmazonCodeBrowser(tcConfig)
) {
logDebugMessage(tcConfig, 'Assuming Amazon code browser');
await handleAmazonCodeBrowser(codeBrowserState, tcConfig);
Expand All @@ -494,9 +482,10 @@ export default defineContentScript({
);
observerForAmazonCodeBrowser.observe(document.body, config);
} else if (
tcConfig.integrationBitBucketCodeBrowser &&
tcConfig.integrations[IntegrationTypes.BITBUCKET].enabled &&
isBitbucketSite(tcConfig)
) {
console.log(tcConfig);
logDebugMessage(tcConfig, 'URL is bitbucket.org - Assuming Bitbucket code browser');
await handleBitbucketCodeBrowser(codeBrowserState, tcConfig);
let observerForBitbucketCodeBrowser = new MutationObserver(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,18 @@
limitations under the License.
******************************************************************************************************************** */

import { Spinner } from '@cloudscape-design/components';
import { FC, useState, useEffect } from 'react';
import { TCConfig, getExtensionConfig } from './config';
import { FC } from 'react';
import { Route, Routes } from 'react-router-dom';
import { ConfigDetailView } from './ConfigDetailView';
import Config from './ConfigView';
import { logDebugMessage } from '../../debugLogger';

const App: FC = () => {
const [config, setConfig] = useState<TCConfig | undefined>();

useEffect(() => {
getExtensionConfig()
.then((loadedConfig) => setConfig(loadedConfig))
.catch((error) => {
logDebugMessage({ debug: true } as any, error);
});
}, []);

return config ? <Config initialConfig={config} /> : <Spinner size="large" />;
return (
<Routes>
<Route key='/' path='/' element={<Config />}/>
<Route key='/integration/:integrationType' path='/integration/:integrationType' element={<ConfigDetailView />}/>
</Routes>
);
};

export default App;
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/** *******************************************************************************************************************
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License").
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************************************************** */
import { Button, SpaceBetween } from '@cloudscape-design/components';
import Form from '@cloudscape-design/components/form';
import { FC, useCallback, useContext, useMemo } from 'react';
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import { IntegrationConfig } from './config';
import { ExtensionConfigContext } from './ExtensionConfigProvider';
import { RegexArrayForm } from './RegexArrayForm';

export interface ConfigDetailViewProps {

}

export const ConfigDetailView: FC<ConfigDetailViewProps> = () => {
const navigate = useNavigate();
const { integrationType } = useParams<{ integrationType: string }>();
const { config, setConfig } = useContext(ExtensionConfigContext);
const integrationConfig = useMemo(() => config.integrations[integrationType!], [integrationType, config]);
const setIntegrationConfig = useCallback((newIntegrationConfig: Partial<IntegrationConfig>) => {
setConfig((prev) => {
return {
...prev, integrations: {
...prev.integrations,
[integrationType!]:
{ ...prev.integrations[integrationType!], ...newIntegrationConfig }
}
};
});
}, [integrationType, setConfig]);
return (
<Form actions={(
<SpaceBetween size="s" direction="horizontal">
<Button onClick={() => {
navigate('/');
}}>Back</Button>
</SpaceBetween>
)}>
<RegexArrayForm placeholder="Regex to apply to URL" strings={integrationConfig.urlRegexes} setStrings={(newRegexes) => { setIntegrationConfig({ urlRegexes: newRegexes }); }}>

</RegexArrayForm>


</Form>

);
};
Loading

0 comments on commit f37a9a0

Please sign in to comment.