ai.certscore/mcp
version 0.2.4
- scanned 2026-10-16
- source github.com/ergoveritas1-alt/certscore.ai
- commit 0a2d7e74d262
- transport stdio
- files read 2886
- methodology 0.2.0
- ruleset 0.1.1
0measured on 98% of this axis
scored on the declared repository, because the published package carries its code only as build output, which is not read as the server's own source; no tag matched this version, so the repository's default branch was read and the findings describe that code, not necessarily the release; 565 findings in test code are listed but not deducted; 9 vendored or minified paths were excluded before scanning; machine-generated bundles are not the server's own code; 26 files could not be parsed and were not scanned
100measured on 65% of this axis
not assessed
no lockfile in the fetched source; dependency versions are unresolved, so no vulnerability can be attributed
100measured on 50% of this axis
53measured on 80% of this axis
The evidence
Every deduction below names the artefact that produced it. Nothing on this page was executed: the source was read, never run, and no running server was contacted.
Code Safety
Showing the 50 most severe of 756 findings on this axis. The score was computed over all of them — findings beyond the fourth of a severity still deduct at a tenth of their weight — so the remainder is omitted from this page, not from the arithmetic.
- apps/validation-worker/scripts/live-consent-audit.ts:1771mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
console.info(`[consent-audit] scenario start ${hostname} ${scenario}`); try { scenarios[scenario] = await runScenario(siteDir, target, scenario); } catch (error) { console.error(`[consent-audit] scenario failed ${hostname} ${scenario}`, error); - apps/validation-worker/scripts/live-consent-audit.ts:1774mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
} catch (error) { console.error(`[consent-audit] scenario failed ${hostname} ${scenario}`, error); scenarios[scenario] = scenarioErrorResult(target, scenario, error); } } - apps/validation-worker/src/full-site/scheduler.ts:427mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
} try { byHost[host] = parseCrawlRobots(robots.text, getCrawlerProductToken()); } catch { diagnostics.push(discoveryDiagnostic("robots", `https://${host}/robots.txt`, "invalid_robots", robots.status)); - apps/validation-worker/src/full-site/scheduler.ts:686mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const routes = { ...options.queueUrls }; for (const region of ["eu-central-1", "eu-west-1", "us-west-1"]) { if (localInventoryEnabled()) routes[region] ||= "local"; } await stopCrawlsWithoutDispatchQueues(routes); - apps/web/components/scans/gdpr-eprivacy-coverage-checklist-card.tsx:2941mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
delete next[rowId]; } else { next[rowId] = "error"; } } - apps/web/components/scanso2/gdpr-eprivacy-coverage-checklist-card.tsx:2954mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
delete next[rowId]; } else { next[rowId] = "error"; } } - apps/web/lib/admin/activity-search.ts:98mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
for (const field of Object.keys(exclusions) as Array<keyof AdminActivitySearchExclusions>) { exclusions[field] = [...new Set(exclusions[field])]; } return { exclusions, query: remaining || null, source }; - apps/web/lib/admin/v2-shadow-preview.ts:276mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const counts: Record<string, number> = {}; for (const value of values) { counts[value] = (counts[value] ?? 0) + 1; } return counts; - apps/web/lib/attribution/campaign-attribution.ts:44mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const value = sanitizeValue(record[key]); if (value) { normalized[key] = value; } } - apps/web/lib/attribution/campaign-attribution.ts:57mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const value = params.get(key); if (value !== null) { values[key] = value; } } - apps/web/lib/pulse/calibration-context.ts:16mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const value = record[key]; if (typeof value === "string") { output[key] = value.slice(0, 80); } else if (typeof value === "number" || typeof value === "boolean" || value === null) { output[key] = value; - apps/web/lib/pulse/calibration-context.ts:18mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
output[key] = value.slice(0, 80); } else if (typeof value === "number" || typeof value === "boolean" || value === null) { output[key] = value; } } - apps/web/lib/pulse/projection.ts:308mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
} if (typeof value === "string") { output[key] = value.slice(0, 240); } else if (typeof value === "number" || typeof value === "boolean" || value === null) { output[key] = value; - apps/web/lib/pulse/projection.ts:310mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
output[key] = value.slice(0, 240); } else if (typeof value === "number" || typeof value === "boolean" || value === null) { output[key] = value; } else if (Array.isArray(value)) { output[key] = value - apps/web/lib/pulse/projection.ts:312mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
output[key] = value; } else if (Array.isArray(value)) { output[key] = value .slice(0, 20) .flatMap((entry) => { - apps/web/lib/pulse/projection.ts:321mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
}); } else if (value && typeof value === "object") { output[key] = safeRecordSubset(value as Record<string, unknown>, Object.keys(value).slice(0, 20)); } } - apps/web/lib/scans/executive-findings-projection.ts:4618mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const value = packet.evidence?.counts?.[key]; if (value !== undefined && (typeof value === "number" || value === null)) { timing[key] = value; } } - apps/web/lib/scans/gdpr-eprivacy-coverage-policy.ts:11724mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
- apps/web/lib/scans/scan-report-review-findings.ts:1045mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const values = mergeStringArrayEvidence(baseFallbackEvidence[key], hybridFallbackEvidence[key]); if (values.length > 0) { merged[key] = values; } } - apps/web/lib/scans/supplemental-runtime-unified-findings.ts:60mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const value = record[key] ?? entities?.[key]; if (value !== undefined) { proof[key] = value; } } - apps/web/lib/scans/unified-findings.ts:2306mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const value = normalizedFallbackEvidence[key]; if (typeof value === "number" && Number.isFinite(value)) { counts[key] = value; } } - apps/web/lib/scans/unified-findings.ts:2320mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const value = summary[key]; if (typeof value === "number" && Number.isFinite(value)) { counts[key] = value; } } - apps/web/lib/scans/unified-findings.ts:2336mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const value = verification[key]; if (typeof value === "number" && Number.isFinite(value)) { counts[key] = value; } } - apps/web/lib/scans/unified-findings.ts:3297mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
]) { if (Array.isArray(summary[key])) { entities[key] = uniqueStrings(summary[key] as string[]); } } - apps/web/scripts/audit-production-evidence-loss.ts:553mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
})); const summary = summarizePromotionBlockers(assessments); drilldowns[findingId] = { assessments, blockerCounts: summary.blockerCounts, - apps/web/scripts/eval/build-finding-corpus.ts:1077mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
} perFindingCounts[fid] = { finding_label: label, total_examples: posList.length + chalList.length, - apps/web/scripts/load-test-safety.ts:290mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
for (const label of entry.interruptionLabels.length > 0 ? entry.interruptionLabels : ["none"]) { group.interruptionLabels[label] = (group.interruptionLabels[label] ?? 0) + 1; } - apps/web/scripts/load-test-safety.ts:375mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
pagesScanned += Math.max(0, entry.pagesScanned ?? 0); for (const label of entry.interruptionLabels.length > 0 ? entry.interruptionLabels : ["none"]) { labelCounts[label] = (labelCounts[label] ?? 0) + 1; } for (const [key, value] of Object.entries(entry.errorCounters ?? {})) { - apps/web/server/ops/load-test-quality-history.ts:93mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
} for (const findingId of seenInScan) { findingScanCounts[findingId] = (findingScanCounts[findingId] ?? 0) + 1; } } - apps/web/server/ops/scanner-quality-normal-history.ts:231mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
} for (const findingId of seenInScan) { findingScanCounts[findingId] = (findingScanCounts[findingId] ?? 0) + 1; } pagesScanned += Math.max(0, row.pagesScanned); - apps/web/server/scans/get-scan-by-id.ts:123mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
all.findIndex((candidate) => JSON.stringify(candidate) === JSON.stringify(value)) === index ); merged[key] = deduped.slice(0, 200); } } - apps/web/server/scans/get-scan-by-id.ts:711mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
if ((currentValue === null || currentValue === undefined || currentValue === false || currentValue === 0) && previewValue !== null && previewValue !== undefined) { merged[field] = previewValue; } } - apps/web/server/scans/local-v2-dag-lambda-dispatch.ts:668mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
: null; if (sha256 && sizeBytes !== null && /^[a-f0-9]{64}$/i.test(sha256) && sizeBytes >= 0) { artifactMetadata[field] = { sha256: sha256.toLowerCase(), sizeBytes - outputs/hosted-oauth-launch-2026-09-14/verify-public.mjs:6mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const paths=['/','/releases','/releases/mcp-hosted-oauth','/releases/mcp-light','/releases/accept-and-reject-path-testing','/developers/mcp','/developers/quickstart','/developers','/mcp/light','/claude','/releases/feed.xml','/sitemap.xml','/llms.txt','/llms-full.txt','/api-pulse-agent-guide.txt']; const results=[];const bodies={}; for(const path of paths){const r=await fetch(origin+path);assert.equal(r.status,200,path);bodies[path]=await r.text();results.push({path,status:r.status});} for(const path of ['/','/releases']){const b=bodies[path]; const slugOrder=['mcp-hosted-oauth','accept-and-reject-path-testing','mcp-light'].map(s=>b.indexOf('/releases/'+s));assert.ok(slugOrder.every(i=>i>=0));assert.ok(slugOrder[0]<slugOrder[1]&&slugOrder[1]<slugOrder[2],path+' order');} for(const path of ['/developers/mcp','/developers/quickstart','/mcp/light','/llms.txt','/llms-full.txt','/api-pulse-agent-guide.txt'])assert.doesNotMatch(bodies[path],/12 scan\/report|Active Trial workspaces connecting|No separate Connect screen|approve the connection once|exactly the three Light tools|certscore_get_scan_bundle only/); - packages/certscore-mcp/src/server.ts:274mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
for (const key of ["scanId", "jobId", "findingId"]) { const token = boundedTelemetryToken(args[key], 128); if (token) values[key] = token; } for (const key of ["maxWaitSeconds", "maxBytes", "maxFindings", "maxPreConsentRows", "maxRows", "limit", "offset"]) { - packages/certscore-mcp/src/server.ts:278mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
for (const key of ["maxWaitSeconds", "maxBytes", "maxFindings", "maxPreConsentRows", "maxRows", "limit", "offset"]) { const value = args[key]; if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 1_000_000) values[key] = value; } if (typeof args.waitForCompletion === "boolean") values.waitForCompletion = args.waitForCompletion; - packages/certscore-mcp/src/server.ts:368mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
for (const key of ["scope", "windowId"] as const) { const value = boundedTelemetryToken(creation?.[key], 128); if (value) projected[key] = value; } for (const key of ["retryAfterSeconds", "limit", "used", "windowSeconds"] as const) { - packages/certscore-mcp/src/server.ts:372mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
for (const key of ["retryAfterSeconds", "limit", "used", "windowSeconds"] as const) { const value = key === "retryAfterSeconds" ? error[key] : creation?.[key]; if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 1_000_000) projected[key] = value; } return projected; - packages/certscore-report-adapter/src/cli/shadow-project.ts:227mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
addCounts(rowsByStatus, site.rowsByStatus); for (const warning of site.sanitizationWarnings) { sanitizationWarningCounts[warning] = (sanitizationWarningCounts[warning] ?? 0) + 1; } for (const moduleRun of site.moduleStatuses) { - packages/certscore-report-adapter/src/cli/shadow-project.ts:236mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
addPreferenceCenterTraversal(preferenceCenterTraversal, site.preferenceCenterTraversal); for (const key of site.topRowKeys) { rowKeyCounts[key] = (rowKeyCounts[key] ?? 0) + 1; } } - packages/certscore-report-adapter/src/cli/shadow-project.ts:394mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
) { for (const key of Object.keys(target) as EndpointGroupKind[]) { target[key] += source[key]; } } - packages/certscore-report-adapter/src/cli/shadow-project.ts:428mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const counts: Record<string, number> = {}; for (const value of values) { counts[value] = (counts[value] ?? 0) + 1; } return counts; - packages/certscore-report-adapter/src/wc01-shadow-output.ts:534mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const counts: Record<string, number> = {}; for (const value of values) { counts[value] = (counts[value] ?? 0) + 1; } return counts; - packages/certscore-report-adapter/src/wc01-v2-allowlist-output.ts:683mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const counts: Record<string, number> = {}; for (const value of values) { counts[value] = (counts[value] ?? 0) + 1; } return counts; - packages/certscore-report-adapter/src/wc01-v2-concern-policy-comparison-output.ts:403mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const counts: Record<string, number> = {}; for (const value of values) { counts[value] = (counts[value] ?? 0) + 1; } return counts; - packages/certscore-report-adapter/src/wc01-v2-concern-policy-input-output.ts:527mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const counts: Record<string, number> = {}; for (const value of values) { counts[value] = (counts[value] ?? 0) + 1; } return counts; - packages/certscore-report-adapter/src/wc01-v2-concern-policy-shape-comparison-output.ts:172mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const counts: Record<string, number> = {}; for (const value of values) { counts[value] = (counts[value] ?? 0) + 1; } return counts; - packages/certscore-report-adapter/src/wc01-v2-concern-policy-simulation-output.ts:413mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const counts: Record<string, number> = {}; for (const value of values) { counts[value] = (counts[value] ?? 0) + 1; } return counts; - packages/certscore-report-adapter/src/wc01-v2-limited-admin-preview-approval-metadata-output.ts:160mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const counts: Record<string, number> = {}; for (const value of values) { counts[value] = (counts[value] ?? 0) + 1; } return counts; - packages/certscore-report-adapter/src/wc01-v2-limited-admin-preview-implementation-proposal-output.ts:174mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
const counts: Record<string, number> = {}; for (const value of values) { counts[value] = (counts[value] ?? 0) + 1; } return counts;
Auth Posture
- authentication_model
stdio transport: the client launches the server as a subprocess, so the OS process boundary is the auth boundary — `03` §4 does not penalise absent auth here
- transport_security
declared transport: stdio — launched as a subprocess by the client, so the security boundary is the client process, not a network port (`03` §4)
- secret_handling
detect-secrets matched 2 credential-shaped string(s), every one from a keyword or entropy heuristic (Base64 High Entropy String, Secret Keyword). Those flag placeholders, variable names and content hashes as readily as credentials, so they do not establish the committed credential `03` §4's 0 asserts, and this sub-check is not scored; Secret Keyword at README.md:49; Base64 High Entropy String at dist/certscore-mcp.mjs:19026
- authorization_granularity
no tool registration was recognised anywhere in the source. An MCP server registers tools by definition, so this is a gap in our detection rather than a server with none, and `03` §4's threshold cannot be applied to it.
Maintenance
- recency
last commit 2 day(s) before 2026-10-16
- issue_responsiveness
every issue read was filed by the project itself (2 by its maintainers or bots), so none is someone outside waiting for an answer, and `03` §5's question — will anyone read an issue I file? — has nothing to measure.
- release_cadence
released 21 day(s) ago (by releases), inside `03` §5's 30-day window
- bus_factor
this repository's 12-month history is longer than the bounded walk `04` §7 performs, and the authors found so far span more than one of `03` §5's bands. A limit of ours, not a finding about the server.
- repository_signals
`03` §5 scores this by quartile WITHIN the server's category, which needs a distribution across every scanned server and so cannot be computed while scanning one. It is a post-pass over the crawl and that pass does not exist yet.
Transparency
- license
LICENSE present with no SPDX-License-Identifier tag; text not matched against any license this build can identify (`04` §7's classifier is not wired; see module docstring)
- readme_quality
README.md: installation instructions (+25), configuration documented (+25), exposed tools or resources enumerated (+30), states what the server does in the first 200 words (+20)
- declared_scopes
specific scope or access documentation is present, but `03` §7's 100 and 60 bands both compare it against scopes INFERRED from static analysis, which is not built yet. Scoring 100 on an empty inference would be a vacuous truth — 'all ∅ inferred scopes are documented'.
- changelog
no CHANGELOG in the fetched source. `03` §7's partial credit for tagged releases carrying release notes is not applied here — that needs the forge API, which this check does not call
- security_contact
no SECURITY.md and no disclosure contact in the README, so a finder has no route to report a vulnerability privately
Contest a finding
Every number here comes from a rule reading published source, and rules get things wrong. If something on this page is incorrect — or correct but mitigated by a check the rule cannot see — open an issue and we answer it in writing. A finding we got wrong is removed and the rule is fixed; one that stands is answered on the record.
Anyone can file: the maintainer, a researcher who disagrees with the classification, or a user who tested the server and could not reproduce it. Name this server and the rule or advisory ID shown beside the finding above. We aim to reply within five business days and to resolve within ten.