Skip to content
This repository was archived by the owner on Jan 22, 2019. It is now read-only.

Commit ff75251

Browse files
authored
fix: prevent transitive importing of entrypoints (#184)
1 parent bb0c01b commit ff75251

File tree

14 files changed

+80
-60
lines changed

14 files changed

+80
-60
lines changed

src/config/link.entry.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
window.EXTENSION_ENV = 'LINK'

src/config/options.entry.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
window.EXTENSION_ENV = 'OPTIONS'

src/extension/envAssertion.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export function assertEnv(env: typeof window['EXTENSION_ENV']): void {
2+
if (window.EXTENSION_ENV !== env) {
3+
throw new Error(
4+
'Detected transitive import of an entrypoint! ' +
5+
window.EXTENSION_ENV +
6+
' attempted to import a file that is only intended to be imported by ' +
7+
env +
8+
'.'
9+
)
10+
}
11+
}

src/extension/scripts/background.tsx

Lines changed: 29 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ import storage, { defaultStorageItems } from '../../browser/storage'
1515
import * as tabs from '../../browser/tabs'
1616
import initializeCli from '../../libs/cli'
1717
import { resolveClientConfiguration } from '../../shared/backend/server'
18-
import { isBackground } from '../../shared/context'
18+
import { ExtensionConnectionInfo, onFirstMessage } from '../../shared/messaging'
1919
import { DEFAULT_SOURCEGRAPH_URL, setSourcegraphUrl } from '../../shared/util/context'
20+
import { assertEnv } from '../envAssertion'
21+
22+
assertEnv('BACKGROUND')
2023

2124
let customServerOrigins: string[] = []
2225

@@ -439,52 +442,29 @@ const spawnAndConnect = ({
439442
connectPortAndWorker(port, worker)
440443
})
441444

442-
/**
443-
* The information necessary to connect to a Sourcegraph extension.
444-
*/
445-
export interface ExtensionConnectionInfo {
446-
extensionID: string
447-
jsBundleURL: string
448-
}
449-
450-
/**
451-
* Executes the callback only on the first message that's received on the port.
452-
*/
453-
export const onFirstMessage = (port: chrome.runtime.Port, callback: (message: any) => void) => {
454-
const cb = message => {
455-
port.onMessage.removeListener(cb)
456-
callback(message)
457-
}
458-
port.onMessage.addListener(cb)
459-
}
460-
461-
// This must not execute anywhere but the background script, otherwise messages
462-
// will get duplicated and cause all kinds of unintuitive behavior.
463-
if (isBackground) {
464-
// This is the bridge between content scripts (that want to connect to Sourcegraph extensions) and the background
465-
// script (that spawns JS bundles or connects to WebSocket endpoints).:
466-
chrome.runtime.onConnect.addListener(port => {
467-
// When a content script wants to create a connection to a Sourcegraph extension, it first connects to the
468-
// background script on a random port and sends a message containing the platform information for that
469-
// Sourcegraph extension (e.g. a JS bundle at localhost:1234/index.js).
470-
onFirstMessage(port, (connectionInfo: ExtensionConnectionInfo) => {
471-
// The background script receives the message and attempts to spawn the
472-
// extension:
473-
spawnAndConnect({ connectionInfo, port }).then(
474-
// If spawning succeeds, the background script sends {} (so the content script knows it succeeded) and
475-
// the port communicates using the internal Sourcegraph extension RPC API after that.
476-
() => {
477-
// Success is represented by the absence of an error
478-
port.postMessage({})
479-
},
480-
// If spawning fails, the background script sends { error } (so the content script knows it failed) and
481-
// the port is immediately disconnected. There is always a 1-1 correspondence between ports and content
482-
// scripts, so this won't disrupt any other connections.
483-
error => {
484-
port.postMessage({ error })
485-
port.disconnect()
486-
}
487-
)
488-
})
445+
// This is the bridge between content scripts (that want to connect to Sourcegraph extensions) and the background
446+
// script (that spawns JS bundles or connects to WebSocket endpoints).:
447+
chrome.runtime.onConnect.addListener(port => {
448+
// When a content script wants to create a connection to a Sourcegraph extension, it first connects to the
449+
// background script on a random port and sends a message containing the platform information for that
450+
// Sourcegraph extension (e.g. a JS bundle at localhost:1234/index.js).
451+
onFirstMessage(port, (connectionInfo: ExtensionConnectionInfo) => {
452+
// The background script receives the message and attempts to spawn the
453+
// extension:
454+
spawnAndConnect({ connectionInfo, port }).then(
455+
// If spawning succeeds, the background script sends {} (so the content script knows it succeeded) and
456+
// the port communicates using the internal Sourcegraph extension RPC API after that.
457+
() => {
458+
// Success is represented by the absence of an error
459+
port.postMessage({})
460+
},
461+
// If spawning fails, the background script sends { error } (so the content script knows it failed) and
462+
// the port is immediately disconnected. There is always a 1-1 correspondence between ports and content
463+
// scripts, so this won't disrupt any other connections.
464+
error => {
465+
port.postMessage({ error })
466+
port.disconnect()
467+
}
468+
)
489469
})
490-
}
470+
})

src/extension/scripts/inject.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import { injectBitbucketServer } from '../../libs/bitbucket/inject'
2020
import { injectGitHubApplication } from '../../libs/github/inject'
2121
import { injectPhabricatorApplication } from '../../libs/phabricator/app'
2222
import { injectSourcegraphApp } from '../../libs/sourcegraph/inject'
23+
import { assertEnv } from '../envAssertion'
24+
25+
assertEnv('CONTENT')
2326

2427
/**
2528
* Main entry point into browser extension.

src/extension/scripts/link.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import storage from '../../browser/storage'
2+
import { assertEnv } from '../envAssertion'
3+
4+
assertEnv('LINK')
25

36
const searchParams = new URLSearchParams(window.location.search)
47

src/extension/scripts/options.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import * as React from 'react'
66
import { render } from 'react-dom'
77
import storage from '../../browser/storage'
88
import { OptionsDashboard } from '../../shared/components/options/OptionsDashboard'
9+
import { assertEnv } from '../envAssertion'
10+
11+
assertEnv('OPTIONS')
912

1013
const inject = () => {
1114
const injectDOM = document.createElement('div')

src/shared/backend/PortMessageTransports.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
MessageReader,
88
MessageWriter,
99
} from 'sourcegraph/module/protocol/jsonrpc2/transport'
10-
import { ExtensionConnectionInfo } from '../../extension/scripts/background'
10+
import { ExtensionConnectionInfo } from '../messaging'
1111

1212
class PortMessageReader extends AbstractMessageReader implements MessageReader {
1313
private pending: Message[] = []

src/shared/backend/extensions.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import {
1111
Settings,
1212
} from '@sourcegraph/extensions-client-common/lib/settings'
1313
import { LoadingSpinner } from '@sourcegraph/react-loading-spinner'
14-
import * as JSONC from '@sqs/jsonc-parser'
1514
import { applyEdits } from '@sqs/jsonc-parser'
15+
import * as JSONC from '@sqs/jsonc-parser'
1616
import { removeProperty, setProperty } from '@sqs/jsonc-parser/lib/edit'
1717
import { isEqual } from 'lodash'
1818
import Alert from 'mdi-react/AlertIcon'
@@ -25,8 +25,7 @@ import { TextDocumentDecoration } from 'sourcegraph/module/protocol/plainTypes'
2525
import uuid from 'uuid'
2626
import { Disposable } from 'vscode-languageserver'
2727
import storage, { StorageItems } from '../../browser/storage'
28-
import { onFirstMessage } from '../../extension/scripts/background'
29-
import { ExtensionConnectionInfo } from '../../extension/scripts/background'
28+
import { ExtensionConnectionInfo, onFirstMessage } from '../messaging'
3029
import { getContext } from './context'
3130
import { createAggregateError, isErrorLike } from './errors'
3231
import { queryGraphQL } from './graphql'

src/extension/scripts/extensions.tsx renamed to src/shared/components/options/BrowserSettingsEditor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// We want to polyfill first.
2-
import '../../config/polyfill'
2+
import '../../../config/polyfill'
33

44
import * as React from 'react'
55
import { Button, FormGroup, Input, Label } from 'reactstrap'
66
import { Subscription } from 'rxjs'
7-
import storage from '../../browser/storage'
7+
import storage from '../../../browser/storage'
88

99
interface State {
1010
clientSettings: string

0 commit comments

Comments
 (0)