(ctx context.Context, serverName, lowerID, lowerVersion string, client *http.Client, index *serviceIndex)
| 205 | } |
| 206 | |
| 207 | func validateReadme(ctx context.Context, serverName, lowerID, lowerVersion string, client *http.Client, index *serviceIndex) (ReadmeState, error) { |
| 208 | readmeURLTemplate, err := getReadmeURLTemplate(index) |
| 209 | if err != nil { |
| 210 | return NoReadme, fmt.Errorf("failed to get README URL template: %w", err) |
| 211 | } |
| 212 | |
| 213 | // Replace placeholders in the template. PathEscape both the id and version |
| 214 | // so a publisher cannot smuggle "/" / ".." through the template into a |
| 215 | // fetch against an unrelated package's README. |
| 216 | readmeURL := strings.ReplaceAll(readmeURLTemplate, "{lower_id}", url.PathEscape(lowerID)) |
| 217 | readmeURL = strings.ReplaceAll(readmeURL, "{lower_version}", url.PathEscape(lowerVersion)) |
| 218 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, readmeURL, nil) |
| 219 | if err != nil { |
| 220 | return NoReadme, fmt.Errorf("failed to create NuGet README request: %w", err) |
| 221 | } |
| 222 | |
| 223 | req.Header.Set("User-Agent", userAgent) |
| 224 | |
| 225 | resp, err := client.Do(req) |
| 226 | if err != nil { |
| 227 | return NoReadme, fmt.Errorf("failed to fetch NuGet README: %w", err) |
| 228 | } |
| 229 | defer resp.Body.Close() |
| 230 | |
| 231 | if resp.StatusCode == http.StatusOK { |
| 232 | // Check README content |
| 233 | readmeBytes, err := io.ReadAll(resp.Body) |
| 234 | if err != nil { |
| 235 | return NoReadme, fmt.Errorf("failed to read NuGet README content: %w", err) |
| 236 | } |
| 237 | |
| 238 | readmeContent := string(readmeBytes) |
| 239 | |
| 240 | // Check for mcp-name: format (more specific) |
| 241 | mcpNamePattern := "mcp-name: " + serverName |
| 242 | if strings.Contains(readmeContent, mcpNamePattern) { |
| 243 | return ValidReadme, nil // Found as mcp-name: format |
| 244 | } |
| 245 | |
| 246 | return InvalidReadme, nil |
| 247 | } |
| 248 | |
| 249 | if resp.StatusCode == http.StatusNotFound { |
| 250 | return NoReadme, nil |
| 251 | } |
| 252 | |
| 253 | return InvalidReadme, fmt.Errorf("NuGet README request returned status %d", resp.StatusCode) |
| 254 | } |
| 255 | |
| 256 | func getPackageContentBaseURL(index *serviceIndex) (string, error) { |
| 257 | for _, resource := range index.Resources { |
no test coverage detected
searching dependent graphs…