mirror of
https://github.com/bitwarden/clients.git
synced 2026-07-13 21:19:12 +08:00
[SM-1800] Handle corrupted encrypted project names gracefully - Clients (#18873)
* Handle corrupted encrypted project names gracefully - Add decryptionError flag to ProjectView, ProjectListView, and SecretProjectView - Wrap all project name decryption in try-catch blocks across services - Use DECRYPT_ERROR constant instead of hardcoded strings - Display corrupted names as clickable warning text in projects list - Show warning badges for corrupted project names in secrets list - Add i18n keys for error tooltips - Add keyboard accessibility (role, tabindex, enter/space handlers) * fixing tests * fixing jittery loading
This commit is contained in:
parent
2d702e0eac
commit
79c52ca29b
@ -8581,6 +8581,14 @@
|
||||
"message": "New project",
|
||||
"description": "Title for creating a new project."
|
||||
},
|
||||
"clickToRenameProject": {
|
||||
"message": "Click to rename this project",
|
||||
"description": "Tooltip text for corrupted project names that can be clicked to rename."
|
||||
},
|
||||
"cannotDecryptProjectName": {
|
||||
"message": "Cannot decrypt project name - click project to rename",
|
||||
"description": "Tooltip text for project badges when the project name cannot be decrypted."
|
||||
},
|
||||
"softDeleteSecretWarning": {
|
||||
"message": "Deleting secrets can affect existing integrations.",
|
||||
"description": "Warns that deleting secrets can have consequences on integrations"
|
||||
|
||||
@ -9,4 +9,5 @@ export class ProjectListView {
|
||||
read: boolean;
|
||||
write: boolean;
|
||||
linkable: boolean;
|
||||
decryptionError: boolean = false;
|
||||
}
|
||||
|
||||
@ -8,4 +8,5 @@ export class ProjectView {
|
||||
revisionDate: string;
|
||||
read: boolean;
|
||||
write: boolean;
|
||||
decryptionError: boolean = false;
|
||||
}
|
||||
|
||||
@ -3,4 +3,5 @@
|
||||
export class SecretProjectView {
|
||||
id: string;
|
||||
name: string;
|
||||
decryptionError: boolean = false;
|
||||
}
|
||||
|
||||
@ -48,7 +48,9 @@ export class ProjectDialogComponent implements OnInit {
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private router: Router,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
) {
|
||||
this.loading = data.operation === OperationType.Edit;
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
if (this.data.operation === OperationType.Edit && this.data.projectId) {
|
||||
|
||||
@ -7,7 +7,10 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||
import {
|
||||
DECRYPT_ERROR,
|
||||
EncString,
|
||||
} from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
@ -136,10 +139,15 @@ export class ProjectService {
|
||||
projectView.revisionDate = projectResponse.revisionDate;
|
||||
projectView.read = projectResponse.read;
|
||||
projectView.write = projectResponse.write;
|
||||
projectView.name = await this.encryptService.decryptString(
|
||||
new EncString(projectResponse.name),
|
||||
orgKey,
|
||||
);
|
||||
try {
|
||||
projectView.name = await this.encryptService.decryptString(
|
||||
new EncString(projectResponse.name),
|
||||
orgKey,
|
||||
);
|
||||
} catch {
|
||||
projectView.name = DECRYPT_ERROR;
|
||||
projectView.decryptionError = true;
|
||||
}
|
||||
return projectView;
|
||||
}
|
||||
|
||||
@ -155,10 +163,15 @@ export class ProjectService {
|
||||
projectListView.organizationId = s.organizationId;
|
||||
projectListView.read = s.read;
|
||||
projectListView.write = s.write;
|
||||
projectListView.name = await this.encryptService.decryptString(
|
||||
new EncString(s.name),
|
||||
orgKey,
|
||||
);
|
||||
try {
|
||||
projectListView.name = await this.encryptService.decryptString(
|
||||
new EncString(s.name),
|
||||
orgKey,
|
||||
);
|
||||
} catch {
|
||||
projectListView.name = DECRYPT_ERROR;
|
||||
projectListView.decryptionError = true;
|
||||
}
|
||||
projectListView.creationDate = s.creationDate;
|
||||
projectListView.revisionDate = s.revisionDate;
|
||||
projectListView.linkable = true;
|
||||
|
||||
@ -130,6 +130,7 @@ const secretView: SecretView = {
|
||||
{
|
||||
id: "502d93ae-a084-490a-8a64-b187015eb69c",
|
||||
name: "project-name",
|
||||
decryptionError: false,
|
||||
},
|
||||
],
|
||||
read: true,
|
||||
@ -156,6 +157,7 @@ const expectedSecretView: SecretView = {
|
||||
{
|
||||
id: "502d93ae-a084-490a-8a64-b187015eb69c",
|
||||
name: mockUnencryptedData,
|
||||
decryptionError: false,
|
||||
},
|
||||
],
|
||||
read: true,
|
||||
|
||||
@ -7,7 +7,10 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||
import {
|
||||
DECRYPT_ERROR,
|
||||
EncString,
|
||||
} from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
@ -270,9 +273,14 @@ export class SecretService {
|
||||
projects.map(async (s: SecretProjectResponse) => {
|
||||
const projectsMappedToSecretView = new SecretProjectView();
|
||||
projectsMappedToSecretView.id = s.id;
|
||||
projectsMappedToSecretView.name = s.name
|
||||
? await this.encryptService.decryptString(new EncString(s.name), orgKey)
|
||||
: null;
|
||||
try {
|
||||
projectsMappedToSecretView.name = s.name
|
||||
? await this.encryptService.decryptString(new EncString(s.name), orgKey)
|
||||
: null;
|
||||
} catch {
|
||||
projectsMappedToSecretView.name = DECRYPT_ERROR;
|
||||
projectsMappedToSecretView.decryptionError = true;
|
||||
}
|
||||
return projectsMappedToSecretView;
|
||||
}),
|
||||
);
|
||||
|
||||
@ -7,7 +7,10 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||
import {
|
||||
DECRYPT_ERROR,
|
||||
EncString,
|
||||
} from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
@ -147,7 +150,11 @@ export class SecretsManagerPortingApiService {
|
||||
exportData.projects.map(async (p) => {
|
||||
const project = new SecretsManagerExportProject();
|
||||
project.id = p.id;
|
||||
project.name = await this.encryptService.decryptString(new EncString(p.name), orgKey);
|
||||
try {
|
||||
project.name = await this.encryptService.decryptString(new EncString(p.name), orgKey);
|
||||
} catch {
|
||||
project.name = DECRYPT_ERROR;
|
||||
}
|
||||
return project;
|
||||
}),
|
||||
);
|
||||
|
||||
@ -7,7 +7,10 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||
import {
|
||||
DECRYPT_ERROR,
|
||||
EncString,
|
||||
} from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
@ -359,15 +362,21 @@ export class AccessPolicyService {
|
||||
organizationKey: SymmetricCryptoKey,
|
||||
response: GrantedProjectAccessPolicyResponse,
|
||||
): Promise<GrantedProjectAccessPolicyView> {
|
||||
let projectName = null;
|
||||
if (response.grantedProjectName) {
|
||||
try {
|
||||
projectName = await this.encryptService.decryptString(
|
||||
new EncString(response.grantedProjectName),
|
||||
organizationKey,
|
||||
);
|
||||
} catch {
|
||||
projectName = DECRYPT_ERROR;
|
||||
}
|
||||
}
|
||||
return {
|
||||
...this.createBaseAccessPolicyView(response),
|
||||
grantedProjectId: response.grantedProjectId,
|
||||
grantedProjectName: response.grantedProjectName
|
||||
? await this.encryptService.decryptString(
|
||||
new EncString(response.grantedProjectName),
|
||||
organizationKey,
|
||||
)
|
||||
: null,
|
||||
grantedProjectName: projectName,
|
||||
};
|
||||
}
|
||||
|
||||
@ -432,9 +441,15 @@ export class AccessPolicyService {
|
||||
view.currentUserInGroup = r.currentUserInGroup;
|
||||
|
||||
if (r.type === "serviceAccount" || r.type === "project") {
|
||||
view.name = r.name
|
||||
? await this.encryptService.decryptString(new EncString(r.name), orgKey)
|
||||
: null;
|
||||
if (r.name) {
|
||||
try {
|
||||
view.name = await this.encryptService.decryptString(new EncString(r.name), orgKey);
|
||||
} catch {
|
||||
view.name = DECRYPT_ERROR;
|
||||
}
|
||||
} else {
|
||||
view.name = null;
|
||||
}
|
||||
} else {
|
||||
view.name = r.name;
|
||||
}
|
||||
|
||||
@ -62,13 +62,30 @@
|
||||
<div class="tw-flex tw-items-center tw-gap-4 tw-break-all">
|
||||
<i class="bwi bwi-collection tw-text-muted" aria-hidden="true"></i>
|
||||
<div>
|
||||
<span
|
||||
*ngIf="project.decryptionError"
|
||||
class="tw-cursor-pointer tw-text-warning tw-underline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
[title]="'clickToRenameProject' | i18n"
|
||||
[attr.aria-label]="'clickToRenameProject' | i18n"
|
||||
(click)="editProjectEvent.emit(project.id); $event.stopPropagation()"
|
||||
(keydown.enter)="editProjectEvent.emit(project.id); $event.stopPropagation()"
|
||||
(keydown.space)="
|
||||
editProjectEvent.emit(project.id); $event.stopPropagation(); $event.preventDefault()
|
||||
"
|
||||
>
|
||||
{{ project.name }}
|
||||
<i class="bwi bwi-pencil-square tw-ml-1" aria-hidden="true"></i>
|
||||
</span>
|
||||
<a
|
||||
*ngIf="project.linkable"
|
||||
*ngIf="!project.decryptionError && project.linkable"
|
||||
bitLink
|
||||
[routerLink]="['/sm', project.organizationId, 'projects', project.id]"
|
||||
>{{ project.name }}</a
|
||||
>
|
||||
<span *ngIf="!project.linkable">{{ project.name }}</span>
|
||||
{{ project.name }}
|
||||
</a>
|
||||
<span *ngIf="!project.decryptionError && !project.linkable">{{ project.name }}</span>
|
||||
<div class="tw-text-sm tw-text-muted tw-block">
|
||||
{{ project.id }}
|
||||
<button
|
||||
|
||||
@ -85,11 +85,16 @@
|
||||
<span
|
||||
*ngFor="let project of row.projects"
|
||||
bitBadge
|
||||
variant="secondary"
|
||||
[variant]="project.decryptionError ? 'warning' : 'secondary'"
|
||||
class="tw-ml-1"
|
||||
[title]="project.name"
|
||||
[title]="project.decryptionError ? ('cannotDecryptProjectName' | i18n) : project.name"
|
||||
maxWidthClass="tw-max-w-60"
|
||||
>
|
||||
<i
|
||||
*ngIf="project.decryptionError"
|
||||
class="bwi bwi-exclamation-triangle tw-mr-1"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{{ project.name }}
|
||||
</span>
|
||||
<span *ngIf="row.projects.length === 0" bitBadge variant="warning" class="tw-ml-1"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user