@ian2018cs/agenthub 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/server/routes/skills.js +63 -4
package/package.json
CHANGED
package/server/routes/skills.js
CHANGED
|
@@ -13,7 +13,7 @@ const router = express.Router();
|
|
|
13
13
|
const SKILL_NAME_REGEX = /^[a-zA-Z0-9_-]{1,100}$/;
|
|
14
14
|
|
|
15
15
|
// Trusted git hosting domains
|
|
16
|
-
const TRUSTED_GIT_HOSTS = ['github.com', 'gitlab.com', 'bitbucket.org'];
|
|
16
|
+
const TRUSTED_GIT_HOSTS = ['github.com', 'gitlab.com', 'bitbucket.org', 'git.amberweather.com'];
|
|
17
17
|
|
|
18
18
|
// Configure multer for zip file uploads
|
|
19
19
|
const upload = multer({
|
|
@@ -84,6 +84,30 @@ async function isValidSkill(skillPath) {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Check if URL is SSH format (git@host:path)
|
|
89
|
+
*/
|
|
90
|
+
function isSshUrl(url) {
|
|
91
|
+
return /^[a-zA-Z0-9_-]+@[a-zA-Z0-9.-]+:.+$/.test(url);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Parse SSH URL format (git@host:owner/repo.git)
|
|
96
|
+
*/
|
|
97
|
+
function parseSshUrl(url) {
|
|
98
|
+
const match = url.match(/^[a-zA-Z0-9_-]+@([a-zA-Z0-9.-]+):(.+)$/);
|
|
99
|
+
if (!match) return null;
|
|
100
|
+
|
|
101
|
+
const host = match[1].toLowerCase();
|
|
102
|
+
const pathPart = match[2].replace(/\.git$/, '');
|
|
103
|
+
const pathParts = pathPart.split('/');
|
|
104
|
+
|
|
105
|
+
if (pathParts.length >= 2) {
|
|
106
|
+
return { host, owner: pathParts[0], repo: pathParts[1] };
|
|
107
|
+
}
|
|
108
|
+
return { host, owner: null, repo: null };
|
|
109
|
+
}
|
|
110
|
+
|
|
87
111
|
/**
|
|
88
112
|
* Validate GitHub URL
|
|
89
113
|
*/
|
|
@@ -92,6 +116,23 @@ function validateGitUrl(url) {
|
|
|
92
116
|
return { valid: false, error: 'Repository URL is required' };
|
|
93
117
|
}
|
|
94
118
|
|
|
119
|
+
// Handle SSH format: git@host:owner/repo.git
|
|
120
|
+
if (isSshUrl(url)) {
|
|
121
|
+
const parsed = parseSshUrl(url);
|
|
122
|
+
if (!parsed) {
|
|
123
|
+
return { valid: false, error: 'Invalid SSH URL format' };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!TRUSTED_GIT_HOSTS.includes(parsed.host)) {
|
|
127
|
+
return {
|
|
128
|
+
valid: false,
|
|
129
|
+
error: `Only trusted git hosts are allowed: ${TRUSTED_GIT_HOSTS.join(', ')}`
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return { valid: true, isSsh: true };
|
|
134
|
+
}
|
|
135
|
+
|
|
95
136
|
let parsedUrl;
|
|
96
137
|
try {
|
|
97
138
|
parsedUrl = new URL(url);
|
|
@@ -99,11 +140,20 @@ function validateGitUrl(url) {
|
|
|
99
140
|
return { valid: false, error: 'Invalid URL format' };
|
|
100
141
|
}
|
|
101
142
|
|
|
102
|
-
|
|
103
|
-
|
|
143
|
+
const host = parsedUrl.hostname.toLowerCase();
|
|
144
|
+
const allowedProtocols = ['https:', 'http:'];
|
|
145
|
+
|
|
146
|
+
// Allow http protocol only for git.amberweather.com
|
|
147
|
+
if (host === 'git.amberweather.com') {
|
|
148
|
+
if (!allowedProtocols.includes(parsedUrl.protocol)) {
|
|
149
|
+
return { valid: false, error: 'Only HTTPS or HTTP URLs are allowed for this host' };
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
if (parsedUrl.protocol !== 'https:') {
|
|
153
|
+
return { valid: false, error: 'Only HTTPS URLs are allowed' };
|
|
154
|
+
}
|
|
104
155
|
}
|
|
105
156
|
|
|
106
|
-
const host = parsedUrl.hostname.toLowerCase();
|
|
107
157
|
if (!TRUSTED_GIT_HOSTS.includes(host)) {
|
|
108
158
|
return {
|
|
109
159
|
valid: false,
|
|
@@ -118,6 +168,15 @@ function validateGitUrl(url) {
|
|
|
118
168
|
* Extract owner and repo from git URL
|
|
119
169
|
*/
|
|
120
170
|
function parseGitUrl(url) {
|
|
171
|
+
// Handle SSH format
|
|
172
|
+
if (isSshUrl(url)) {
|
|
173
|
+
const parsed = parseSshUrl(url);
|
|
174
|
+
if (parsed && parsed.owner && parsed.repo) {
|
|
175
|
+
return { owner: parsed.owner, repo: parsed.repo };
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
121
180
|
const parsedUrl = new URL(url);
|
|
122
181
|
const pathParts = parsedUrl.pathname.replace(/^\//, '').replace(/\.git$/, '').split('/');
|
|
123
182
|
if (pathParts.length >= 2) {
|