mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 4d91d96e5e121b38d33508cdef17868bab255eae
This commit is contained in:
committed by
AzureDataStudio
parent
a971aee5bd
commit
5e7071e466
@@ -13,7 +13,7 @@ const SecondLevelDomainMatcher = /([^@:.]+\.[^@:.]+)(:\d+)?$/;
|
||||
const RemoteMatcher = /^\s*url\s*=\s*(.+\S)\s*$/mg;
|
||||
const AnyButDot = /[^.]/g;
|
||||
|
||||
export const SecondLevelDomainWhitelist = [
|
||||
export const AllowedSecondLevelDomains = [
|
||||
'github.com',
|
||||
'bitbucket.org',
|
||||
'visualstudio.com',
|
||||
@@ -54,7 +54,7 @@ function extractDomain(url: string): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getDomainsOfRemotes(text: string, whitelist: string[]): string[] {
|
||||
export function getDomainsOfRemotes(text: string, allowedDomains: readonly string[]): string[] {
|
||||
const domains = new Set<string>();
|
||||
let match: RegExpExecArray | null;
|
||||
while (match = RemoteMatcher.exec(text)) {
|
||||
@@ -64,16 +64,9 @@ export function getDomainsOfRemotes(text: string, whitelist: string[]): string[]
|
||||
}
|
||||
}
|
||||
|
||||
const whitemap = whitelist.reduce((map, key) => {
|
||||
map[key] = true;
|
||||
return map;
|
||||
}, Object.create(null));
|
||||
|
||||
const elements: string[] = [];
|
||||
domains.forEach(e => elements.push(e));
|
||||
|
||||
return elements
|
||||
.map(key => whitemap[key] ? key : key.replace(AnyButDot, 'a'));
|
||||
const allowedDomainsSet = new Set(allowedDomains);
|
||||
return Array.from(domains)
|
||||
.map(key => allowedDomainsSet.has(key) ? key : key.replace(AnyButDot, 'a'));
|
||||
}
|
||||
|
||||
function stripPort(authority: string): string | null {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
|
||||
export class GlobalExtensionEnablementService extends Disposable implements IGlobalExtensionEnablementService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private _onDidChangeEnablement = new Emitter<{ readonly extensions: IExtensionIdentifier[], readonly source?: string }>();
|
||||
readonly onDidChangeEnablement: Event<{ readonly extensions: IExtensionIdentifier[], readonly source?: string }> = this._onDidChangeEnablement.event;
|
||||
|
||||
@@ -225,7 +225,7 @@ function getCoreTranslationAssets(version: IRawGalleryExtensionVersion): [string
|
||||
function getRepositoryAsset(version: IRawGalleryExtensionVersion): IGalleryExtensionAsset | null {
|
||||
if (version.properties) {
|
||||
const results = version.properties.filter(p => p.key === AssetType.Repository);
|
||||
const gitRegExp = new RegExp('((git|ssh|http(s)?)|(git@[\w\.]+))(:(//)?)([\w\.@\:/\-~]+)(\.git)(/)?');
|
||||
const gitRegExp = new RegExp('((git|ssh|http(s)?)|(git@[\w.]+))(:(//)?)([\w.@\:/\-~]+)(.git)(/)?');
|
||||
|
||||
const uri = results.filter(r => gitRegExp.test(r.value))[0];
|
||||
return uri ? { uri: uri.value, fallbackUri: uri.value } : null;
|
||||
@@ -371,7 +371,7 @@ interface IRawExtensionsReport {
|
||||
|
||||
export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private extensionsGalleryUrl: string | undefined;
|
||||
private extensionsControlUrl: string | undefined;
|
||||
|
||||
@@ -12,7 +12,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IExtensionManifest, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExeBasedExtensionTip } from 'vs/platform/product/common/productService';
|
||||
|
||||
export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$';
|
||||
export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$';
|
||||
export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN);
|
||||
|
||||
export interface IGalleryExtensionProperties {
|
||||
@@ -153,7 +153,7 @@ export interface ITranslation {
|
||||
}
|
||||
|
||||
export interface IExtensionGalleryService {
|
||||
_serviceBrand: undefined;
|
||||
readonly _serviceBrand: undefined;
|
||||
isEnabled(): boolean;
|
||||
query(token: CancellationToken): Promise<IPager<IGalleryExtension>>;
|
||||
query(options: IQueryOptions, token: CancellationToken): Promise<IPager<IGalleryExtension>>;
|
||||
@@ -200,7 +200,7 @@ export class ExtensionManagementError extends Error {
|
||||
}
|
||||
|
||||
export interface IExtensionManagementService {
|
||||
_serviceBrand: undefined;
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
onInstallExtension: Event<InstallExtensionEvent>;
|
||||
onDidInstallExtension: Event<DidInstallExtensionEvent>;
|
||||
@@ -225,7 +225,7 @@ export const ENABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/enabled';
|
||||
export const IGlobalExtensionEnablementService = createDecorator<IGlobalExtensionEnablementService>('IGlobalExtensionEnablementService');
|
||||
|
||||
export interface IGlobalExtensionEnablementService {
|
||||
_serviceBrand: undefined;
|
||||
readonly _serviceBrand: undefined;
|
||||
readonly onDidChangeEnablement: Event<{ readonly extensions: IExtensionIdentifier[], readonly source?: string }>;
|
||||
|
||||
getDisabledExtensions(): IExtensionIdentifier[];
|
||||
@@ -246,7 +246,7 @@ export type IWorkspaceTips = { readonly remoteSet: string[]; readonly recommenda
|
||||
|
||||
export const IExtensionTipsService = createDecorator<IExtensionTipsService>('IExtensionTipsService');
|
||||
export interface IExtensionTipsService {
|
||||
_serviceBrand: undefined;
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
getConfigBasedTips(folder: URI): Promise<IConfigBasedExtensionTip[]>;
|
||||
getImportantExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]>;
|
||||
|
||||
@@ -77,7 +77,7 @@ export class ExtensionManagementChannel implements IServerChannel {
|
||||
|
||||
export class ExtensionManagementChannelClient implements IExtensionManagementService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
private readonly channel: IChannel,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { tmpdir } from 'os';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files';
|
||||
import { IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
@@ -20,9 +19,9 @@ const ExtensionIdVersionRegex = /^([^.]+\..+)-(\d+\.\d+\.\d+)$/;
|
||||
|
||||
export class ExtensionsDownloader extends Disposable {
|
||||
|
||||
private readonly extensionsDownloadDir: URI = URI.file(tmpdir());
|
||||
private readonly cache: number = 0;
|
||||
private readonly cleanUpPromise: Promise<void> = Promise.resolve();
|
||||
private readonly extensionsDownloadDir: URI;
|
||||
private readonly cache: number;
|
||||
private readonly cleanUpPromise: Promise<void>;
|
||||
|
||||
constructor(
|
||||
@IEnvironmentService environmentService: INativeEnvironmentService,
|
||||
@@ -31,11 +30,9 @@ export class ExtensionsDownloader extends Disposable {
|
||||
@ILogService private readonly logService: ILogService,
|
||||
) {
|
||||
super();
|
||||
if (environmentService.extensionsDownloadPath) {
|
||||
this.extensionsDownloadDir = URI.file(environmentService.extensionsDownloadPath);
|
||||
this.cache = 20; // Cache 20 downloads
|
||||
this.cleanUpPromise = this.cleanUp();
|
||||
}
|
||||
this.extensionsDownloadDir = URI.file(environmentService.extensionsDownloadPath);
|
||||
this.cache = 20; // Cache 20 downloads
|
||||
this.cleanUpPromise = this.cleanUp();
|
||||
}
|
||||
|
||||
async downloadExtension(extension: IGalleryExtension, operation: InstallOperation): Promise<URI> {
|
||||
@@ -46,10 +43,7 @@ export class ExtensionsDownloader extends Disposable {
|
||||
}
|
||||
|
||||
async delete(location: URI): Promise<void> {
|
||||
// Delete immediately if caching is disabled
|
||||
if (!this.cache) {
|
||||
await this.fileService.del(location);
|
||||
}
|
||||
// noop as caching is enabled always
|
||||
}
|
||||
|
||||
private async download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<void> {
|
||||
|
||||
@@ -61,7 +61,7 @@ interface InstallableExtension {
|
||||
|
||||
export class ExtensionManagementService extends Disposable implements IExtensionManagementService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly extensionsScanner: ExtensionsScanner;
|
||||
private reportedExtensions: Promise<IReportedExtension[]> | undefined;
|
||||
@@ -178,7 +178,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
if (identifierWithVersion.equals(new ExtensionIdentifierWithVersion(existing.identifier, existing.manifest.version))) {
|
||||
return this.extensionsScanner.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart Azure Data Studio before reinstalling {0}.", manifest.displayName || manifest.name))));
|
||||
} else if (semver.gt(existing.manifest.version, manifest.version)) {
|
||||
return this.uninstall(existing, true);
|
||||
return this.uninstallExtension(existing);
|
||||
}
|
||||
} else {
|
||||
// Remove the extension with same version if it is already uninstalled.
|
||||
@@ -309,7 +309,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
.then(local => this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)).finally(() => { }).then(() => local));
|
||||
})
|
||||
.then(local => this.installDependenciesAndPackExtensions(local, existingExtension)
|
||||
.then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error))))
|
||||
.then(() => local, error => this.uninstall(local).then(() => Promise.reject(error), () => Promise.reject(error))))
|
||||
.then(
|
||||
async local => {
|
||||
if (existingExtension && semver.neq(existingExtension.manifest.version, extension.version)) {
|
||||
@@ -480,11 +480,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
return this.getInstalled(ExtensionType.User)
|
||||
.then(installed =>
|
||||
Promise.all(installed.filter(local => extensions.some(galleryExtension => new ExtensionIdentifierWithVersion(local.identifier, local.manifest.version).equals(new ExtensionIdentifierWithVersion(galleryExtension.identifier, galleryExtension.version)))) // Check with version because we want to rollback the exact version
|
||||
.map(local => this.uninstall(local, true))))
|
||||
.map(local => this.uninstall(local))))
|
||||
.then(() => undefined, () => undefined);
|
||||
}
|
||||
|
||||
uninstall(extension: ILocalExtension, force = false): Promise<void> {
|
||||
uninstall(extension: ILocalExtension): Promise<void> {
|
||||
this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id);
|
||||
return this.toNonCancellablePromise(this.getInstalled(ExtensionType.User)
|
||||
.then(installed => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { getDomainsOfRemotes, getRemotes } from 'vs/platform/extensionManagement
|
||||
|
||||
suite('Config Remotes', () => {
|
||||
|
||||
const whitelist = [
|
||||
const allowedDomains = [
|
||||
'github.com',
|
||||
'github2.com',
|
||||
'github3.com',
|
||||
@@ -20,37 +20,37 @@ suite('Config Remotes', () => {
|
||||
];
|
||||
|
||||
test('HTTPS remotes', function () {
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://github.com/Microsoft/vscode.git'), whitelist), ['github.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://git.example.com/gitproject.git'), whitelist), ['example.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username@github2.com/username/repository.git'), whitelist), ['github2.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username:password@github3.com/username/repository.git'), whitelist), ['github3.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username:password@example2.com:1234/username/repository.git'), whitelist), ['example2.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://example3.com:1234/username/repository.git'), whitelist), ['example3.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://github.com/Microsoft/vscode.git'), allowedDomains), ['github.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://git.example.com/gitproject.git'), allowedDomains), ['example.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username@github2.com/username/repository.git'), allowedDomains), ['github2.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username:password@github3.com/username/repository.git'), allowedDomains), ['github3.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username:password@example2.com:1234/username/repository.git'), allowedDomains), ['example2.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('https://example3.com:1234/username/repository.git'), allowedDomains), ['example3.com']);
|
||||
});
|
||||
|
||||
test('SSH remotes', function () {
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('ssh://user@git.server.org/project.git'), whitelist), ['server.org']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('ssh://user@git.server.org/project.git'), allowedDomains), ['server.org']);
|
||||
});
|
||||
|
||||
test('SCP-like remotes', function () {
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('git@github.com:Microsoft/vscode.git'), whitelist), ['github.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('user@git.server.org:project.git'), whitelist), ['server.org']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('git.server2.org:project.git'), whitelist), ['server2.org']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('git@github.com:Microsoft/vscode.git'), allowedDomains), ['github.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('user@git.server.org:project.git'), allowedDomains), ['server.org']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('git.server2.org:project.git'), allowedDomains), ['server2.org']);
|
||||
});
|
||||
|
||||
test('Local remotes', function () {
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('/opt/git/project.git'), whitelist), []);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('file:///opt/git/project.git'), whitelist), []);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('/opt/git/project.git'), allowedDomains), []);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(remote('file:///opt/git/project.git'), allowedDomains), []);
|
||||
});
|
||||
|
||||
test('Multiple remotes', function () {
|
||||
const config = ['https://github.com/Microsoft/vscode.git', 'https://git.example.com/gitproject.git'].map(remote).join('');
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(config, whitelist).sort(), ['example.com', 'github.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(config, allowedDomains).sort(), ['example.com', 'github.com']);
|
||||
});
|
||||
|
||||
test('Whitelisting', () => {
|
||||
test('Non allowed domains are anonymized', () => {
|
||||
const config = ['https://github.com/Microsoft/vscode.git', 'https://git.foobar.com/gitproject.git'].map(remote).join('');
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(config, whitelist).sort(), ['aaaaaa.aaa', 'github.com']);
|
||||
assert.deepStrictEqual(getDomainsOfRemotes(config, allowedDomains).sort(), ['aaaaaa.aaa', 'github.com']);
|
||||
});
|
||||
|
||||
test('HTTPS remotes to be hashed', function () {
|
||||
|
||||
@@ -15,6 +15,8 @@ suite('Extension Identifier Pattern', () => {
|
||||
assert.equal(true, regEx.test('PUBLISHER.NAME'));
|
||||
assert.equal(true, regEx.test('PUBLISHEr.NAMe'));
|
||||
assert.equal(true, regEx.test('PUBLISHEr.N-AMe'));
|
||||
assert.equal(true, regEx.test('PUB-LISHEr.NAMe'));
|
||||
assert.equal(true, regEx.test('PUB-LISHEr.N-AMe'));
|
||||
assert.equal(true, regEx.test('PUBLISH12Er90.N-A54Me123'));
|
||||
assert.equal(true, regEx.test('111PUBLISH12Er90.N-1111A54Me123'));
|
||||
assert.equal(false, regEx.test('publishername'));
|
||||
|
||||
Reference in New Issue
Block a user