Browse Source

Fixes #2207 protects circular JSON.stringify

Adds container, commit, branch, tag, and view node protection to the default resolver for @gate & @memoize decorators
main
Eric Amodio 2 years ago
parent
commit
d31dc97820
10 changed files with 82 additions and 36 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +4
    -0
      src/container.ts
  3. +3
    -6
      src/extension.ts
  4. +5
    -0
      src/git/models/branch.ts
  5. +5
    -0
      src/git/models/commit.ts
  6. +5
    -0
      src/git/models/tag.ts
  7. +15
    -17
      src/logger.ts
  8. +2
    -2
      src/system/decorators/log.ts
  9. +40
    -9
      src/system/decorators/resolver.ts
  10. +2
    -2
      src/views/nodes/viewNode.ts

+ 1
- 0
CHANGELOG.md View File

@ -29,6 +29,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Fixed
- Fixes [#2207](https://github.com/gitkraken/vscode-gitlens/issues/2207) - Error when trying to push individual commit
- Fixes [#2301](https://github.com/gitkraken/vscode-gitlens/issues/2301) - Create Worktree button doesn't work in certain cases
- Fixes [#2318](https://github.com/gitkraken/vscode-gitlens/issues/2318) - GitLens need to login again after VS Code insiders upgrade every day
- Fixes [#2377](https://github.com/gitkraken/vscode-gitlens/issues/2377) - Missing Azure Devops Icon

+ 4
- 0
src/container.ts View File

@ -729,3 +729,7 @@ export class Container {
});
}
}
export function isContainer(container: any): container is Container {
return container instanceof Container;
}

+ 3
- 6
src/extension.ts View File

@ -10,8 +10,9 @@ import { Commands, ContextKeys, CoreCommands } from './constants';
import { Container } from './container';
import { setContext } from './context';
import { isGitUri } from './git/gitUri';
import { getBranchNameWithoutRemote } from './git/models/branch';
import { getBranchNameWithoutRemote, isBranch } from './git/models/branch';
import { isCommit } from './git/models/commit';
import { isTag } from './git/models/tag';
import { Logger, LogLevel } from './logger';
import {
showDebugLoggingWarningMessage,
@ -42,11 +43,7 @@ export async function activate(context: ExtensionContext): Promise
})`;
}
if (isCommit(o)) {
return `GitCommit(${o.sha ? ` sha=${o.sha}` : ''}${o.repoPath ? ` repoPath=${o.repoPath}` : ''})`;
}
if (isViewNode(o)) return o.toString();
if (isBranch(o) || isCommit(o) || isTag(o) || isViewNode(o)) return o.toString();
return undefined;
});

+ 5
- 0
src/git/models/branch.ts View File

@ -1,5 +1,6 @@
import { BranchSorting, configuration, DateStyle } from '../../configuration';
import { Container } from '../../container';
import { getLoggableName } from '../../logger';
import { formatDate, fromNow } from '../../system/date';
import { debug } from '../../system/decorators/log';
import { memoize } from '../../system/decorators/memoize';
@ -76,6 +77,10 @@ export class GitBranch implements GitBranchReference {
};
}
toString(): string {
return `${getLoggableName(this)}(${this.id})`;
}
get formattedDate(): string {
return Container.instance.BranchDateFormatting.dateStyle === DateStyle.Absolute
? this.formatDate(Container.instance.BranchDateFormatting.dateFormat)

+ 5
- 0
src/git/models/commit.ts View File

@ -4,6 +4,7 @@ import type { GravatarDefaultStyle } from '../../configuration';
import { DateSource, DateStyle } from '../../configuration';
import { GlyphChars } from '../../constants';
import type { Container } from '../../container';
import { getLoggableName } from '../../logger';
import { formatDate, fromNow } from '../../system/date';
import { gate } from '../../system/decorators/gate';
import { memoize } from '../../system/decorators/memoize';
@ -111,6 +112,10 @@ export class GitCommit implements GitRevisionReference {
}
}
toString(): string {
return `${getLoggableName(this)}(${this.repoPath}|${this.shortSha})`;
}
get date(): Date {
return this.container.CommitDateFormatting.dateSource === DateSource.Committed
? this.committer.date

+ 5
- 0
src/git/models/tag.ts View File

@ -1,5 +1,6 @@
import { configuration, DateStyle, TagSorting } from '../../configuration';
import { Container } from '../../container';
import { getLoggableName } from '../../logger';
import { formatDate, fromNow } from '../../system/date';
import { memoize } from '../../system/decorators/memoize';
import { sortCompare } from '../../system/string';
@ -29,6 +30,10 @@ export class GitTag implements GitTagReference {
this.id = getTagId(repoPath, name);
}
toString(): string {
return `${getLoggableName(this)}(${this.id})`;
}
get formattedDate(): string {
return Container.instance.TagDateFormatting.dateStyle === DateStyle.Absolute
? this.formatDate(Container.instance.TagDateFormatting.dateFormat)

+ 15
- 17
src/logger.ts View File

@ -193,23 +193,6 @@ export class Logger {
}
}
static toLoggableName(instance: Function | object) {
let name: string;
if (typeof instance === 'function') {
if (instance.prototype == null || instance.prototype.constructor == null) {
return instance.name;
}
name = instance.prototype.constructor.name ?? emptyStr;
} else {
name = instance.constructor?.name ?? emptyStr;
}
// Strip webpack module name (since I never name classes with an _)
const index = name.indexOf('_');
return index === -1 ? name : name.substr(index + 1);
}
private static get timestamp(): string {
return `[${new Date().toISOString().replace(/T/, ' ').slice(0, -1)}]`;
}
@ -282,3 +265,18 @@ function toOrderedLevel(logLevel: LogLevel): OrderedLevel {
return OrderedLevel.Off;
}
}
export function getLoggableName(instance: Function | object) {
let name: string;
if (typeof instance === 'function') {
if (instance.prototype?.constructor == null) return instance.name;
name = instance.prototype.constructor.name ?? emptyStr;
} else {
name = instance.constructor?.name ?? emptyStr;
}
// Strip webpack module name (since I never name classes with an _)
const index = name.indexOf('_');
return index === -1 ? name : name.substr(index + 1);
}

+ 2
- 2
src/system/decorators/log.ts View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
import { hrtime } from '@env/hrtime';
import type { LogScope } from '../../logger';
import { Logger, LogLevel } from '../../logger';
import { getLoggableName, Logger, LogLevel } from '../../logger';
import { getParameters } from '../function';
import { isPromise } from '../promise';
import { getDurationMilliseconds } from '../string';
@ -155,7 +155,7 @@ export function log any>(options?: LogOptions, deb
let instanceName: string;
if (this != null) {
instanceName = Logger.toLoggableName(this);
instanceName = getLoggableName(this);
if (this.constructor?.[LogInstanceNameFn]) {
instanceName = target.constructor[LogInstanceNameFn](this, instanceName);
}

+ 40
- 9
src/system/decorators/resolver.ts View File

@ -1,9 +1,16 @@
import { Uri } from 'vscode';
import { isContainer } from '../../container';
import { isBranch } from '../../git/models/branch';
import { isCommit } from '../../git/models/commit';
import { isTag } from '../../git/models/tag';
import { isViewNode } from '../../views/nodes/viewNode';
function replacer(key: string, value: any): any {
if (key === '') return value;
if (value == null) return value;
if (typeof value !== 'object') return value;
if (value instanceof Error) return String(value);
if (value instanceof Uri) {
if ('sha' in (value as any) && (value as any).sha) {
@ -11,6 +18,12 @@ function replacer(key: string, value: any): any {
}
return value.toString();
}
if (isBranch(value) || isCommit(value) || isTag(value) || isViewNode(value)) {
return value.toString();
}
if (isContainer(value)) {
return '<container>';
}
return value;
}
@ -23,16 +36,34 @@ export function defaultResolver(...args: any[]): string {
const arg0 = args[0];
if (arg0 == null) return '';
if (typeof arg0 === 'string') return arg0;
if (typeof arg0 === 'number' || typeof arg0 === 'boolean' || arg0 instanceof Error) return String(arg0);
if (arg0 instanceof Uri) {
if ('sha' in (arg0 as any) && (arg0 as any).sha) {
return `${(arg0 as any).sha}:${arg0.toString()}`;
}
return arg0.toString();
}
switch (typeof arg0) {
case 'string':
return arg0;
case 'number':
case 'boolean':
case 'undefined':
case 'symbol':
case 'bigint':
return String(arg0);
return JSON.stringify(arg0, replacer);
default:
if (arg0 instanceof Error) return String(arg0);
if (arg0 instanceof Uri) {
if ('sha' in arg0 && typeof arg0.sha === 'string' && arg0.sha) {
return `${arg0.sha}:${arg0.toString()}`;
}
return arg0.toString();
}
if (isBranch(arg0) || isCommit(arg0) || isTag(arg0) || isViewNode(arg0)) {
return arg0.toString();
}
if (isContainer(arg0)) {
return '<container>';
}
return JSON.stringify(arg0, replacer);
}
}
export type Resolver<T extends (...arg: any) => any> = (...args: Parameters<T>) => string;

+ 2
- 2
src/views/nodes/viewNode.ts View File

@ -10,7 +10,7 @@ import { GitReference } from '../../git/models/reference';
import { GitRemote } from '../../git/models/remote';
import type { RepositoryChangeEvent } from '../../git/models/repository';
import { Repository, RepositoryChange, RepositoryChangeComparisonMode } from '../../git/models/repository';
import { Logger } from '../../logger';
import { getLoggableName } from '../../logger';
import type { SubscriptionChangeEvent } from '../../plus/subscription/subscriptionService';
import { gate } from '../../system/decorators/gate';
import { debug, log, logName } from '../../system/decorators/log';
@ -96,7 +96,7 @@ export abstract class ViewNode
toString(): string {
const id = this.id;
return `${Logger.toLoggableName(this)}${id != null ? `(${id})` : ''}`;
return `${getLoggableName(this)}${id != null ? `(${id})` : ''}`;
}
protected _uri: GitUri;

Loading…
Cancel
Save