Skip to content

Commit a836611

Browse files
committed
feat: add ManifestValidator contract
1 parent 8a36950 commit a836611

1 file changed

Lines changed: 112 additions & 0 deletions

File tree

src/core/ManifestValidator.sol

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
/// @title ManifestValidator
5+
/// @notice Validates EthPM v3 manifest structure and naming conventions (EIP-2678)
6+
library ManifestValidator {
7+
8+
/// @notice Validate contract name matches EIP-2678 regex: ^[a-zA-Z_$][a-zA-Z0-9_$]{0,255}$
9+
/// @param name Contract name to validate
10+
/// @return valid True if name is valid
11+
function isValidContractName(string memory name) internal pure returns (bool) {
12+
bytes memory nameBytes = bytes(name);
13+
14+
// Must have at least 1 character, max 256
15+
if (nameBytes.length == 0 || nameBytes.length > 256) {
16+
return false;
17+
}
18+
19+
// First character must be: a-z, A-Z, _, $
20+
bytes1 first = nameBytes[0];
21+
bool validFirst = (first >= 0x61 && first <= 0x7A) || // a-z
22+
(first >= 0x41 && first <= 0x5A) || // A-Z
23+
(first == 0x5F) || // _
24+
(first == 0x24); // $
25+
26+
if (!validFirst) {
27+
return false;
28+
}
29+
30+
// Rest can be: a-z, A-Z, 0-9, _, $
31+
for (uint256 i = 1; i < nameBytes.length; i++) {
32+
bytes1 char = nameBytes[i];
33+
bool validChar = (char >= 0x61 && char <= 0x7A) || // a-z
34+
(char >= 0x41 && char <= 0x5A) || // A-Z
35+
(char >= 0x30 && char <= 0x39) || // 0-9
36+
(char == 0x5F) || // _
37+
(char == 0x24); // $
38+
39+
if (!validChar) {
40+
return false;
41+
}
42+
}
43+
44+
return true;
45+
}
46+
47+
/// @notice Validate package name matches EIP-2678: lowercase, numbers, hyphens only
48+
/// @param name Package name to validate
49+
/// @return valid True if name is valid
50+
function isValidPackageName(string memory name) internal pure returns (bool) {
51+
bytes memory nameBytes = bytes(name);
52+
53+
if (nameBytes.length == 0) {
54+
return false;
55+
}
56+
57+
for (uint256 i = 0; i < nameBytes.length; i++) {
58+
bytes1 char = nameBytes[i];
59+
60+
bool isLowercase = (char >= 0x61 && char <= 0x7A); // a-z
61+
bool isNumber = (char >= 0x30 && char <= 0x39); // 0-9
62+
bool isHyphen = (char == 0x2D); // -
63+
64+
if (!isLowercase && !isNumber && !isHyphen) {
65+
return false;
66+
}
67+
}
68+
69+
return true;
70+
}
71+
72+
/// @notice Validate contract alias matches EIP-2678: <contract-name> or <contract-name><identifier>
73+
/// @param contractAlias Contract alias to validate
74+
/// @return valid True if alias is valid
75+
function isValidContractAlias(string memory contractAlias) internal pure returns (bool) {
76+
bytes memory aliasBytes = bytes(contractAlias);
77+
if (aliasBytes.length == 0 || aliasBytes.length > 256) {
78+
return false;
79+
}
80+
81+
// Alias can contain: a-z, A-Z, 0-9, -, _
82+
for (uint256 i = 0; i < aliasBytes.length; i++) {
83+
bytes1 char = aliasBytes[i];
84+
85+
bool isLetter = (char >= 0x61 && char <= 0x7A) || (char >= 0x41 && char <= 0x5A);
86+
bool isNumber = (char >= 0x30 && char <= 0x39);
87+
bool isHyphen = (char == 0x2D);
88+
bool isUnderscore = (char == 0x5F);
89+
90+
if (!isLetter && !isNumber && !isHyphen && !isUnderscore) {
91+
return false;
92+
}
93+
}
94+
95+
return true;
96+
}
97+
98+
/// @notice Validate manifest version is "ethpm/3"
99+
/// @param version Manifest version string
100+
/// @return valid True if version is correct
101+
function isValidManifestVersion(string memory version) internal pure returns (bool) {
102+
return keccak256(bytes(version)) == keccak256(bytes("ethpm/3"));
103+
}
104+
105+
/// @notice Check if key is forbidden in v3 manifest
106+
/// @param key Manifest key to check
107+
/// @return forbidden True if key is forbidden
108+
function isForbiddenKey(string memory key) internal pure returns (bool) {
109+
// "manifest_version" is forbidden in v3, must use "manifest"
110+
return keccak256(bytes(key)) == keccak256(bytes("manifest_version"));
111+
}
112+
}

0 commit comments

Comments
 (0)