Skip to content

feat: added a cli tool and docs for use in editing the files#1711

Open
blurpesec wants to merge 4 commits intomasterfrom
mh/add-cli-to-update-tokens
Open

feat: added a cli tool and docs for use in editing the files#1711
blurpesec wants to merge 4 commits intomasterfrom
mh/add-cli-to-update-tokens

Conversation

@blurpesec
Copy link
Contributor

@blurpesec blurpesec commented Feb 2, 2026

This pull request introduces a new CLI tool for managing contract metadata and icons according to the CAIP-19 standard. The tool provides commands to add, update, verify, and list assets, and includes thorough documentation and workflow instructions. The changes also add supporting dependencies and update project scripts to make the CLI easily accessible.

CLI Tool Introduction and Documentation

  • Added comprehensive instructions and usage examples for the new CLI tool in .github/copilot-instructions.md, CLAUDE.md, and README.md, detailing commands for adding, updating, verifying, and listing CAIP-19 assets, as well as workflow and file structure notes. [1] [2]

Project Scripts and Dependencies

  • Updated package.json to add scripts for running the CLI tool (asset, asset:set, asset:verify, asset:list) and included the zod dependency for input validation. [1] [2]

Note

Medium Risk
Adds a new Node-based CLI that writes/deletes files under metadata/ and icons/ and can download remote images, so mistakes or bad inputs could modify repository assets unexpectedly. Changes are self-contained and mainly additive, but introduce new operational surface area and a new dependency (zod).

Overview
Introduces a new CLI (cli-update-asset.js) to manage CAIP-19 assets: set to create/update metadata and icons (including optional URL-based image download and cleanup of old icon files), verify to validate metadata/icon consistency, and list to enumerate assets by namespace.

Exposes the tool via new npm scripts (asset:*) and adds zod for input/metadata validation. Adds contributor-facing documentation in README.md, .github/copilot-instructions.md, plus pointer files (.cursorrules, CLAUDE.md) describing the workflow and command usage.

Written by Cursor Bugbot for commit 08930a0. This will update automatically on new commits. Configure here.

@blurpesec blurpesec requested review from a team and MRabenda as code owners February 2, 2026 22:25
@socket-security
Copy link

socket-security bot commented Feb 2, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedzod@​4.3.610010010095100

View full report

@socket-security
Copy link

socket-security bot commented Feb 2, 2026

Warning

MetaMask internal reviewing guidelines:

  • Do not ignore-all
  • Each alert has instructions on how to review if you don't know what it means. If lost, ask your Security Liaison or the supply-chain group
  • Copy-paste ignore lines for specific packages or a group of one kind with a note on what research you did to deem it safe.
    @SocketSecurity ignore npm/PACKAGE@VERSION
Action Severity Alert  (click "▶" to expand/collapse)
Warn Low
Potential code anomaly (AI signal): npm zod is 100.0% likely to have a medium risk anomaly

Notes: No explicit network exfiltration, reverse shell, or credential theft is present in this fragment. However, the code assembles and compiles arbitrary code via the Function constructor and invokes passed-in functions immediately (twice). That behavior constitutes a strong dangerous primitive (arbitrary code execution) which can be abused if any inputs (strings or args) are attacker-controlled. Treat this module as risky in threat models where inputs are not fully trusted; review call sites and sanitize/validate inputs or avoid dynamic evaluation.

Confidence: 1.00

Severity: 0.60

From: package.jsonnpm/zod@4.3.6

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/zod@4.3.6. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

fs.unlinkSync(oldIcon);
console.log(`✓ Removed old icon: ${oldIcon}`);
}
}
Copy link

Choose a reason for hiding this comment

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

Old icon files not deleted when updating format

Medium Severity

When updating an asset's icon with a different image format, the old icon file may not be deleted. The code calls findExistingIcon AFTER saving the new icon, and since findExistingIcon iterates through extensions in a fixed order (['.svg', '.png', '.jpg', '.jpeg']), it will find the newly saved icon first if its extension comes earlier in the array. This causes the old icon (e.g., .png) to remain when replaced by a format with higher priority (e.g., .svg), leaving orphaned files in the icons directory.

Fix in Cursor Fix in Web

Choose a reason for hiding this comment

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

@copilot @blurpesec good test and catch on where the order of operations for the stack is important in call up when using the CLI. We will intergrate a solution that allows for both cases when the order of operations can be switched up in the callstack, and when it cannot be.

The fix and where the "missing" error handler needs to be placed and created is this: the findExisitingIcon could be placed first in the stack as best case scenario fix. This will be done by the function FlipSortCLC (Command Line Command) @copilot will code this for us.It's something like grepping "FindExistingIcon" inside the CLI file itself, along with C programming tactics to maintain the events as designed originally in the CLI without editing/writing directly to the file to test how will that works in handling errors by logging and comparing how many errors exist further in a test log that gets generated that counts the error emission that the run gives before FlipSORTCLC is used, and after FlipSortCLC it is used by forking to a test env in parellel. This will allow for testing what is the best in placing the FindExistingIcon in the callstack for @blurpesec in an A/B test of the two .env run in parellel (the main one and the test) now regardless of a flip or not, we must make as a function calledFindOrphanedIcons as the new error handle fallback that gets called if an error (like the one above) is emitted and points to the findExistingIcon function as worse case scenario.

Human written solution. DOing my best here.

const metadataLogoPath = path.basename(metadata.logo);
if (expectedLogoPath !== metadataLogoPath) {
issues.push(`Logo path mismatch: metadata references "${metadataLogoPath}" but file is "${expectedLogoPath}"`);
}
Copy link

Choose a reason for hiding this comment

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

Verify command crashes on missing logo field

Low Severity

The verifyAsset function crashes with a TypeError when the metadata file is missing the logo field but an icon file exists. After validateMetadata detects the missing field and adds it to the issues array, execution continues to line 440 where path.basename(metadata.logo) is called. Since metadata.logo is undefined, Node's path.basename() throws TypeError: The "path" argument must be of type string. The verify command fails to complete and report all found issues.

Fix in Cursor Fix in Web

Choose a reason for hiding this comment

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

Figuring it out. dunno how well FindOprhanIcon could work potentially here at first. Obviously here and n othing has been tested formally yet by being ran. Documentation is being given to provide the robust shape for the AI runner, while keeping it normal for the human dev.

Choose a reason for hiding this comment

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

The verification logic doesn't handle the case where metadata.logo is missing/undefined before calling path.basename(metadata.logo). This can cause a TypeError when path.basename receives a non-string value. Add validation to ensure metadata.logo exists before attempting to extract the basename.

VALIDATE IS THE ANSWER! Don't Trust things that the validator hasn't validated. be the validator a human or his clanker.

fileStream.on("error", (err) => {
fs.unlink(destPath, () => {}); // Clean up partial file
reject(err);
});
Copy link

Choose a reason for hiding this comment

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

Download hangs if network fails mid-transfer

Low Severity

The downloadFile function is missing an error handler on the response stream. While fileStream.on("error") and the client's .on("error", reject) handle file and connection errors respectively, errors emitted by the response stream during transfer (e.g., network disconnection mid-download) are not caught. This causes the promise to never settle, leaving the CLI hanging indefinitely instead of failing with an error.

Fix in Cursor Fix in Web

Choose a reason for hiding this comment

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

promises promises promises out in the middle of nowhere. I promise we won't settle for this, we will fulfill the promise.

hold on.

Choose a reason for hiding this comment

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

FindOrphanIcon and FlipSortCLC functions are the current command primitives being made to define the missing error handler for the response stream.

@drqsatoshi
Copy link

drqsatoshi commented Feb 5, 2026

On PR #1712 CAIP-19 successfully in the wild as I was fixing two medium bugs that Cursor caught.

https://github.com/MetaMask/contract-metadata/pull/1712/changes/BASE..ceb3a38b255d682391c1ff00738040320080586c#r2770572259

is the exact moment in which the bug documentation is emitted.

@drqsatoshi
Copy link

drq (.svg highest priotiy)
drq (.jpg lower priority, but all tied to a CAIP-19 normalised token for a baseline at PR #1712

DrQ.svg DrQ.jpg are separate and here so they can be used for the compare experiment. Aka, Dr. Q"s utility if you will as an ERC20 token.

Copy link

Follow-up fixes applied to address Cursor findings + a Zod v4 crash:

  • Icon cleanup on format change: when updating an existing asset image, we now capture all existing icon paths before writing the new icon, then delete any stale formats after a successful copy. This prevents orphaned old icons (e.g. .png remaining after switching to .svg).
  • verify robustness: avoid crashing when metadata.logo is missing; verify reports issues cleanly instead of throwing a TypeError.
  • download reliability: downloadFile now rejects on mid-stream failures (response stream error/aborted) instead of hanging.

Additional hardening bundled with the above:

  • Redirect handling now supports relative Location values and guards against missing Location.
  • Zod v4 compatibility: switched error handling to use ZodError.issues (with fallback) instead of .errors to avoid crashes during validation.

Tests:

  • Added tape regression tests covering icon cleanup + verify missing-logo behavior.
  • Local npm test passes.

Copy link

@drqsatoshi drqsatoshi left a comment

Choose a reason for hiding this comment

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

tl;dr , use validation to validate to the fix. Don't trust yet.

Copy link

@drqsatoshi drqsatoshi left a comment

Choose a reason for hiding this comment

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

this looks good.

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.

2 participants