Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: introduce @trezor/metadata package #16110

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft

Conversation

mroz22
Copy link
Contributor

@mroz22 mroz22 commented Dec 28, 2024

this PR introduces a new package @trezor/metadata. This package is intended to gather all metadata/labelling logic in it so that the suite-mobile team can use it easily when the time comes (cc @matejkriz).

resolve #16124

if (!json?.access_token || !json?.refresh_token) {
throw new Error('Could not retrieve the tokens.');
}
Client.accessToken = json.access_token;
Client.refreshToken = json.refresh_token;
} catch {
// TODO: it looks like that this actually does not work. I tested it even on develop by throwing an artifical error somewhere above.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @komret. not a big deal. if I am not wrong, this doesn't work. at least it helped me discover another potential runtime issue I fixed here https://github.com/trezor/trezor-suite/pull/16110/files#diff-56d44c230c949c29acb06aed65bf1fffef52e38cf29e86546c87b0e92b2c9c25R165

'request-code': (url: string, callback: (credentials: Credentials) => void) => void;
};

export abstract class AbstractMetadataProvider extends TypedEmitter<Events> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just changed it to event emitter. no changes besides that

Copy link

github-actions bot commented Dec 28, 2024

🚀 Expo preview is ready!

  • Project → trezor-suite-preview
  • Platforms → android, ios
  • Scheme → trezorsuitelite
  • Runtime Version → 21
  • More info

Learn more about 𝝠 Expo Github Action

tags: Record<number, PasswordTag>;
};

export type Credentials =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only Credentials type was added

@mroz22 mroz22 force-pushed the metadata-package branch 5 times, most recently from c6ee3b1 to 935948a Compare January 3, 2025 12:17
return Promise.resolve({ success: true as const });
}

delete nextMetadata.outputLabels[payload.txid][payload.outputIndex];

Check warning

Code scanning / CodeQL

Prototype-polluting assignment Medium

This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
library input
.

Copilot Autofix AI about 6 hours ago

To fix the problem, we need to ensure that payload.txid cannot be used to modify Object.prototype. This can be achieved by validating the payload.txid before using it as a key in the nextMetadata.outputLabels object. We will reject any keys that are __proto__, constructor, or prototype.

Suggested changeset 1
packages/metadata/src/api.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/metadata/src/api.ts b/packages/metadata/src/api.ts
--- a/packages/metadata/src/api.ts
+++ b/packages/metadata/src/api.ts
@@ -442,2 +442,5 @@
         if (payload.type === 'outputLabel') {
+            if (typeof payload.txid === 'string' && (payload.txid === '__proto__' || payload.txid === 'constructor' || payload.txid === 'prototype')) {
+                return Promise.resolve({ success: false as const, error: 'Invalid txid' });
+            }
             if (typeof payload.value !== 'string' || payload.value.length === 0) {
EOF
@@ -442,2 +442,5 @@
if (payload.type === 'outputLabel') {
if (typeof payload.txid === 'string' && (payload.txid === '__proto__' || payload.txid === 'constructor' || payload.txid === 'prototype')) {
return Promise.resolve({ success: false as const, error: 'Invalid txid' });
}
if (typeof payload.value !== 'string' || payload.value.length === 0) {
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
nextMetadata.outputLabels[payload.txid] = {};
}

nextMetadata.outputLabels[payload.txid][payload.outputIndex] = payload.value;

Check warning

Code scanning / CodeQL

Prototype-polluting assignment Medium

This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
library input
.

Copilot Autofix AI about 6 hours ago

To fix the prototype pollution issue, we need to ensure that the keys used in object assignments cannot be __proto__, constructor, or prototype. This can be achieved by adding a check before the assignment to reject these keys. Alternatively, we can use a Map object instead of a plain object for nextMetadata.outputLabels.

The best way to fix the problem without changing existing functionality is to add a check before the assignment to ensure the key is not one of the dangerous properties.

Suggested changeset 1
packages/metadata/src/api.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/metadata/src/api.ts b/packages/metadata/src/api.ts
--- a/packages/metadata/src/api.ts
+++ b/packages/metadata/src/api.ts
@@ -456,2 +456,5 @@
             } else {
+                if (payload.txid === '__proto__' || payload.txid === 'constructor' || payload.txid === 'prototype') {
+                    return Promise.resolve({ success: false as const, error: 'Invalid key' });
+                }
                 if (!nextMetadata.outputLabels[payload.txid]) {
EOF
@@ -456,2 +456,5 @@
} else {
if (payload.txid === '__proto__' || payload.txid === 'constructor' || payload.txid === 'prototype') {
return Promise.resolve({ success: false as const, error: 'Invalid key' });
}
if (!nextMetadata.outputLabels[payload.txid]) {
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options

if (payload.type === 'accountLabel') {
if (typeof payload.value !== 'string' || payload.value.length === 0) {
delete nextMetadata.accountLabel;

Check warning

Code scanning / CodeQL

Prototype-polluting assignment Medium

This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
library input
.

Copilot Autofix AI about 6 hours ago

To fix the problem, we need to ensure that the keys used to modify the nextMetadata object cannot include dangerous values like __proto__. One effective way to achieve this is by using a Map object instead of a plain JavaScript object for nextMetadata. Map objects do not have prototype properties and are therefore immune to prototype pollution.

We will replace the plain object nextMetadata with a Map and update the code to use Map methods for setting and deleting values. This change will be made in the addAccountMetadata method.

Suggested changeset 1
packages/metadata/src/api.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/metadata/src/api.ts b/packages/metadata/src/api.ts
--- a/packages/metadata/src/api.ts
+++ b/packages/metadata/src/api.ts
@@ -439,3 +439,3 @@
 
-        const nextMetadata = cloneObject(data ?? DEFAULT_ACCOUNT_METADATA) as AccountLabels;
+        const nextMetadata = new Map(Object.entries(cloneObject(data ?? DEFAULT_ACCOUNT_METADATA) as AccountLabels));
 
@@ -443,3 +443,3 @@
             if (typeof payload.value !== 'string' || payload.value.length === 0) {
-                if (!nextMetadata.outputLabels[payload.txid]) {
+                if (!nextMetadata.get('outputLabels').get(payload.txid)) {
                     // If we try to delete already deleted label it's ok.
@@ -451,12 +451,12 @@
 
-                delete nextMetadata.outputLabels[payload.txid][payload.outputIndex];
-                if (Object.keys(nextMetadata.outputLabels[payload.txid]).length === 0) {
-                    delete nextMetadata.outputLabels[payload.txid];
+                nextMetadata.get('outputLabels').get(payload.txid).delete(payload.outputIndex);
+                if (nextMetadata.get('outputLabels').get(payload.txid).size === 0) {
+                    nextMetadata.get('outputLabels').delete(payload.txid);
                 }
             } else {
-                if (!nextMetadata.outputLabels[payload.txid]) {
-                    nextMetadata.outputLabels[payload.txid] = {};
+                if (!nextMetadata.get('outputLabels').get(payload.txid)) {
+                    nextMetadata.get('outputLabels').set(payload.txid, new Map());
                 }
 
-                nextMetadata.outputLabels[payload.txid][payload.outputIndex] = payload.value;
+                nextMetadata.get('outputLabels').get(payload.txid).set(payload.outputIndex, payload.value);
             }
@@ -466,5 +466,5 @@
             if (typeof payload.value !== 'string' || payload.value.length === 0) {
-                delete nextMetadata.addressLabels[payload.defaultValue];
+                nextMetadata.get('addressLabels').delete(payload.defaultValue);
             } else {
-                nextMetadata.addressLabels[payload.defaultValue] = payload.value;
+                nextMetadata.get('addressLabels').set(payload.defaultValue, payload.value);
             }
@@ -474,5 +474,5 @@
             if (typeof payload.value !== 'string' || payload.value.length === 0) {
-                delete nextMetadata.accountLabel;
+                nextMetadata.delete('accountLabel');
             } else {
-                nextMetadata.accountLabel = payload.value;
+                nextMetadata.set('accountLabel', payload.value);
             }
@@ -489,5 +489,5 @@
             data: {
-                accountLabel: nextMetadata.accountLabel,
-                outputLabels: nextMetadata.outputLabels,
-                addressLabels: nextMetadata.addressLabels,
+                accountLabel: nextMetadata.get('accountLabel'),
+                outputLabels: Object.fromEntries(nextMetadata.get('outputLabels')),
+                addressLabels: Object.fromEntries(nextMetadata.get('addressLabels')),
             },
EOF
@@ -439,3 +439,3 @@

const nextMetadata = cloneObject(data ?? DEFAULT_ACCOUNT_METADATA) as AccountLabels;
const nextMetadata = new Map(Object.entries(cloneObject(data ?? DEFAULT_ACCOUNT_METADATA) as AccountLabels));

@@ -443,3 +443,3 @@
if (typeof payload.value !== 'string' || payload.value.length === 0) {
if (!nextMetadata.outputLabels[payload.txid]) {
if (!nextMetadata.get('outputLabels').get(payload.txid)) {
// If we try to delete already deleted label it's ok.
@@ -451,12 +451,12 @@

delete nextMetadata.outputLabels[payload.txid][payload.outputIndex];
if (Object.keys(nextMetadata.outputLabels[payload.txid]).length === 0) {
delete nextMetadata.outputLabels[payload.txid];
nextMetadata.get('outputLabels').get(payload.txid).delete(payload.outputIndex);
if (nextMetadata.get('outputLabels').get(payload.txid).size === 0) {
nextMetadata.get('outputLabels').delete(payload.txid);
}
} else {
if (!nextMetadata.outputLabels[payload.txid]) {
nextMetadata.outputLabels[payload.txid] = {};
if (!nextMetadata.get('outputLabels').get(payload.txid)) {
nextMetadata.get('outputLabels').set(payload.txid, new Map());
}

nextMetadata.outputLabels[payload.txid][payload.outputIndex] = payload.value;
nextMetadata.get('outputLabels').get(payload.txid).set(payload.outputIndex, payload.value);
}
@@ -466,5 +466,5 @@
if (typeof payload.value !== 'string' || payload.value.length === 0) {
delete nextMetadata.addressLabels[payload.defaultValue];
nextMetadata.get('addressLabels').delete(payload.defaultValue);
} else {
nextMetadata.addressLabels[payload.defaultValue] = payload.value;
nextMetadata.get('addressLabels').set(payload.defaultValue, payload.value);
}
@@ -474,5 +474,5 @@
if (typeof payload.value !== 'string' || payload.value.length === 0) {
delete nextMetadata.accountLabel;
nextMetadata.delete('accountLabel');
} else {
nextMetadata.accountLabel = payload.value;
nextMetadata.set('accountLabel', payload.value);
}
@@ -489,5 +489,5 @@
data: {
accountLabel: nextMetadata.accountLabel,
outputLabels: nextMetadata.outputLabels,
addressLabels: nextMetadata.addressLabels,
accountLabel: nextMetadata.get('accountLabel'),
outputLabels: Object.fromEntries(nextMetadata.get('outputLabels')),
addressLabels: Object.fromEntries(nextMetadata.get('addressLabels')),
},
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

metadata/labeling refactoring
1 participant