[{"data":1,"prerenderedAt":3426},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fsecurity":143,"\u002Fdocs\u002Fsecurity-surround":3422},[4],{"title":5,"path":6,"stem":7,"children":8,"page":32},"Docs","\u002Fdocs","docs",[9,33,58,79,112,117],{"title":10,"path":11,"stem":12,"children":13,"page":32},"Getting Started","\u002Fdocs\u002Fgetting-started","docs\u002Fgetting-started",[14,18,23,28],{"title":10,"path":15,"stem":16,"order":17},"\u002Fdocs\u002Fgetting-started\u002F_dir","docs\u002Fgetting-started\u002F_dir",1,{"title":19,"path":20,"stem":21,"order":22},"Configuration Reference","\u002Fdocs\u002Fgetting-started\u002Fconfiguration","docs\u002Fgetting-started\u002Fconfiguration",2,{"title":24,"path":25,"stem":26,"order":27},"Deployment Guide","\u002Fdocs\u002Fgetting-started\u002Fdeployment","docs\u002Fgetting-started\u002Fdeployment",3,{"title":29,"path":30,"stem":31,"order":17},"Quick Start","\u002Fdocs\u002Fgetting-started\u002Fquick-start","docs\u002Fgetting-started\u002Fquick-start",false,{"title":34,"path":35,"stem":36,"children":37,"page":32},"Guides","\u002Fdocs\u002Fguides","docs\u002Fguides",[38,41,45,49,54],{"title":34,"path":39,"stem":40,"order":22},"\u002Fdocs\u002Fguides\u002F_dir","docs\u002Fguides\u002F_dir",{"title":42,"path":43,"stem":44,"order":22},"Notifications","\u002Fdocs\u002Fguides\u002Fnotifications","docs\u002Fguides\u002Fnotifications",{"title":46,"path":47,"stem":48,"order":17},"Scoring Algorithm","\u002Fdocs\u002Fguides\u002Fscoring","docs\u002Fguides\u002Fscoring",{"title":50,"path":51,"stem":52,"order":53},"Sunset Mode","\u002Fdocs\u002Fguides\u002Fsunset-mode","docs\u002Fguides\u002Fsunset-mode",4,{"title":55,"path":56,"stem":57,"order":27},"Troubleshooting","\u002Fdocs\u002Fguides\u002Ftroubleshooting","docs\u002Fguides\u002Ftroubleshooting",{"title":59,"path":60,"stem":61,"children":62,"page":32},"Project","\u002Fdocs\u002Fproject","docs\u002Fproject",[63,67,71,75],{"title":59,"path":64,"stem":65,"order":66},"\u002Fdocs\u002Fproject\u002F_dir","docs\u002Fproject\u002F_dir",6,{"title":68,"path":69,"stem":70,"order":27},"Changelog","\u002Fdocs\u002Fproject\u002Fchangelog","docs\u002Fproject\u002Fchangelog",{"title":72,"path":73,"stem":74,"order":17},"Contributing","\u002Fdocs\u002Fproject\u002Fcontributing","docs\u002Fproject\u002Fcontributing",{"title":76,"path":77,"stem":78,"order":22},"Contributors","\u002Fdocs\u002Fproject\u002Fcontributors","docs\u002Fproject\u002Fcontributors",{"title":80,"path":81,"stem":82,"children":83,"page":32},"Reference","\u002Fdocs\u002Freference","docs\u002Freference",[84,87,108],{"title":80,"path":85,"stem":86,"order":27},"\u002Fdocs\u002Freference\u002F_dir","docs\u002Freference\u002F_dir",{"title":88,"path":89,"stem":90,"children":91,"page":32},"Api","\u002Fdocs\u002Freference\u002Fapi","docs\u002Freference\u002Fapi",[92,96,100,104],{"title":93,"path":94,"stem":95,"order":22},"API Reference","\u002Fdocs\u002Freference\u002Fapi\u002F_dir","docs\u002Freference\u002Fapi\u002F_dir",{"title":97,"path":98,"stem":99,"order":22},"API Examples","\u002Fdocs\u002Freference\u002Fapi\u002Fexamples","docs\u002Freference\u002Fapi\u002Fexamples",{"title":101,"path":102,"stem":103,"order":53},"API Versioning & Stability Guarantees","\u002Fdocs\u002Freference\u002Fapi\u002Fversioning","docs\u002Freference\u002Fapi\u002Fversioning",{"title":105,"path":106,"stem":107,"order":27},"Common Workflows","\u002Fdocs\u002Freference\u002Fapi\u002Fworkflows","docs\u002Freference\u002Fapi\u002Fworkflows",{"title":109,"path":110,"stem":111,"order":17},"Architecture","\u002Fdocs\u002Freference\u002Farchitecture","docs\u002Freference\u002Farchitecture",{"title":113,"path":114,"stem":115,"order":116},"Release Workflow","\u002Fdocs\u002Freleasing","docs\u002Freleasing",5,{"title":118,"path":119,"stem":120,"children":121,"order":17},"Security Policy","\u002Fdocs\u002Fsecurity","docs\u002Fsecurity\u002Findex",[122,123,127,131,134,137,140],{"title":118,"path":119,"stem":120,"order":17},{"title":124,"path":125,"stem":126,"order":53},"Security","\u002Fdocs\u002Fsecurity\u002F_dir","docs\u002Fsecurity\u002F_dir",{"title":128,"path":129,"stem":130,"order":22},"OWASP ZAP API Scan — Baseline Report","\u002Fdocs\u002Fsecurity\u002Fzap-baseline-20260310","docs\u002Fsecurity\u002Fzap-baseline-20260310",{"title":128,"path":132,"stem":133,"order":27},"\u002Fdocs\u002Fsecurity\u002Fzap-baseline-20260316","docs\u002Fsecurity\u002Fzap-baseline-20260316",{"title":128,"path":135,"stem":136,"order":53},"\u002Fdocs\u002Fsecurity\u002Fzap-baseline-20260323","docs\u002Fsecurity\u002Fzap-baseline-20260323",{"title":128,"path":138,"stem":139,"order":116},"\u002Fdocs\u002Fsecurity\u002Fzap-baseline-20260324","docs\u002Fsecurity\u002Fzap-baseline-20260324",{"title":128,"path":141,"stem":142},"\u002Fdocs\u002Fsecurity\u002Fzap-baseline-20260406","docs\u002Fsecurity\u002Fzap-baseline-20260406",{"id":144,"title":118,"body":145,"description":2885,"extension":3416,"links":3417,"meta":3418,"navigation":3419,"path":119,"seo":3420,"stem":120,"__hash__":3421},"docs\u002Fdocs\u002Fsecurity\u002Findex.md",{"type":146,"value":147,"toc":3394},"minimark",[148,153,162,165,177,181,184,220,223,226,252,258,263,281,285,305,309,316,320,355,359,391,395,497,501,511,516,686,693,700,760,769,780,783,790,831,836,872,880,1071,1079,1539,1545,1637,1641,1650,1656,1665,1829,1851,1861,1865,1883,1908,1930,1943,1949,1959,1984,1990,2603,2612,2628,2632,2658,2663,2770,2776,2780,2783,2874,2880,2967,2997,3001,3016,3020,3068,3072,3079,3105,3109,3307,3331,3351,3357,3361,3390],[149,150,152],"h2",{"id":151},"security-philosophy","Security Philosophy",[154,155,156,157,161],"p",{},"Capacitarr is developed using AI-assisted coding (\"vibe coding\"). We believe this makes rigorous, transparent security practices ",[158,159,160],"strong",{},"more important, not less",". Every line of code — whether human-written or AI-generated — passes through the same gauntlet of static analysis, dependency scanning, container scanning, and dynamic application security testing before it reaches a release.",[154,163,164],{},"This document is our commitment to transparency. Every security exception, suppression, and allowlist entry is individually documented with rationale — not hidden in config files. We want you to be able to audit our security posture yourself.",[154,166,167,170,171,176],{},[158,168,169],{},"We actively welcome independent security assessments."," If you run a scan, penetration test, or code review and find something, we want to hear about it. We will prioritize investigating and remediating any findings brought to our attention. See ",[172,173,175],"a",{"href":174},"#reporting-a-vulnerability","Reporting a Vulnerability"," below for how to reach us.",[149,178,180],{"id":179},"supported-versions","Supported Versions",[154,182,183],{},"Only the latest stable release receives security fixes. Pre-release versions (alpha, beta, RC) are not covered.",[185,186,187,200],"table",{},[188,189,190],"thead",{},[191,192,193,197],"tr",{},[194,195,196],"th",{},"Version",[194,198,199],{},"Supported",[201,202,203,212],"tbody",{},[191,204,205,209],{},[206,207,208],"td",{},"Latest stable (3.x)",[206,210,211],{},"✅",[191,213,214,217],{},[206,215,216],{},"Pre-release (RC, beta)",[206,218,219],{},"❌",[149,221,175],{"id":222},"reporting-a-vulnerability",[154,224,225],{},"If you discover a security vulnerability, please report it privately:",[227,228,229,243],"ol",{},[230,231,232,235,236,242],"li",{},[158,233,234],{},"GitHub:"," Open a ",[172,237,241],{"href":238,"rel":239},"https:\u002F\u002Fgithub.com\u002FGhent\u002Fcapacitarr\u002Fsecurity\u002Fadvisories\u002Fnew",[240],"nofollow","security advisory"," (private by default)",[230,244,245,248,249],{},[158,246,247],{},"Email:"," Send details to the project maintainer listed in ",[172,250,251],{"href":77},"CONTRIBUTORS.md",[154,253,254,257],{},[158,255,256],{},"Do not"," open a public issue for security vulnerabilities.",[259,260,262],"h3",{"id":261},"what-to-include","What to Include",[264,265,266,269,272,275,278],"ul",{},[230,267,268],{},"Description of the vulnerability",[230,270,271],{},"Steps to reproduce",[230,273,274],{},"Affected version(s)",[230,276,277],{},"Potential impact assessment",[230,279,280],{},"Suggested fix (if you have one)",[259,282,284],{"id":283},"response-timeline","Response Timeline",[264,286,287,293,299],{},[230,288,289,292],{},[158,290,291],{},"Acknowledgment:"," Within 72 hours",[230,294,295,298],{},[158,296,297],{},"Initial assessment:"," Within 1 week",[230,300,301,304],{},[158,302,303],{},"Fix release:"," Dependent on severity; critical issues target a patch release within 2 weeks",[149,306,308],{"id":307},"security-model","Security Model",[154,310,311,312,315],{},"Capacitarr is designed as a ",[158,313,314],{},"self-hosted, single-instance"," application for home lab environments. The security model reflects this:",[259,317,319],{"id":318},"authentication","Authentication",[264,321,322,328,339,345],{},[230,323,324,327],{},[158,325,326],{},"Password authentication:"," bcrypt-hashed passwords (cost factor 12)",[230,329,330,333,334,338],{},[158,331,332],{},"JWT sessions:"," HMAC-SHA256 signed tokens with 24-hour expiry. Set ",[335,336,337],"code",{},"JWT_SECRET"," for persistent sessions across restarts",[230,340,341,344],{},[158,342,343],{},"API keys:"," SHA-256 hashed before storage; plaintext shown once on generation and never stored",[230,346,347,350,351,354],{},[158,348,349],{},"Reverse proxy auth:"," Optional trusted header authentication (",[335,352,353],{},"AUTH_HEADER",") for SSO integration (Authelia, Authentik, Organizr)",[259,356,358],{"id":357},"data-protection","Data Protection",[264,360,361,375,381],{},[230,362,363,366,367,370,371,374],{},[158,364,365],{},"Integration API keys:"," Stored in plaintext in the SQLite database. This is an accepted trade-off: full encryption-at-rest would require a master key, adding complexity with minimal practical benefit when the database file is on a user-owned machine. Ensure the ",[335,368,369],{},"\u002Fconfig"," volume has restrictive file permissions (",[335,372,373],{},"chmod 600",")",[230,376,377,380],{},[158,378,379],{},"API key masking:"," Integration API keys are masked in all API responses (only last 4 characters visible)",[230,382,383,386,387,390],{},[158,384,385],{},"Cookie security:"," Set ",[335,388,389],{},"SECURE_COOKIES=true"," when serving over HTTPS",[259,392,394],{"id":393},"network-security","Network Security",[264,396,397,403,413,430,440],{},[230,398,399,402],{},[158,400,401],{},"SSRF protection:"," All user-provided URLs are validated to use HTTP or HTTPS schemes only",[230,404,405,408,409,412],{},[158,406,407],{},"CORS:"," Same-origin by default; explicit ",[335,410,411],{},"CORS_ORIGINS"," configuration required for cross-origin access",[230,414,415,418,419],{},[158,416,417],{},"Rate limiting:"," Three endpoints are rate-limited per IP address to prevent abuse:\n",[264,420,421,424,427],{},[230,422,423],{},"Login: 10 attempts per 15-minute window",[230,425,426],{},"Integration connection test: 30 attempts per 5-minute window",[230,428,429],{},"Engine manual run: 5 attempts per 5-minute window",[230,431,432,435,436,439],{},[158,433,434],{},"Response body limits:"," Upstream API responses are capped at 64 MiB via ",[335,437,438],{},"io.LimitReader"," to prevent denial-of-service from oversized responses",[230,441,442,445,446],{},[158,443,444],{},"Security headers:"," All responses include:\n",[264,447,448,454,462,467,472,477,482,487,492],{},[230,449,450,453],{},[335,451,452],{},"Content-Security-Policy"," — restricts resource loading to same-origin with per-request cryptographic nonces for inline scripts (script-src uses nonce-based allowlisting; connect-src, font-src, frame-ancestors, base-uri, form-action are same-origin)",[230,455,456,459,460,374],{},[335,457,458],{},"Strict-Transport-Security"," — HSTS with 2-year max-age (only when ",[335,461,389],{},[230,463,464],{},[335,465,466],{},"X-Content-Type-Options: nosniff",[230,468,469],{},[335,470,471],{},"X-Frame-Options: DENY",[230,473,474],{},[335,475,476],{},"Referrer-Policy: strict-origin-when-cross-origin",[230,478,479],{},[335,480,481],{},"Permissions-Policy: camera=(), microphone=(), geolocation=()",[230,483,484],{},[335,485,486],{},"Cross-Origin-Opener-Policy: same-origin",[230,488,489],{},[335,490,491],{},"Cross-Origin-Resource-Policy: same-origin",[230,493,494],{},[335,495,496],{},"X-Permitted-Cross-Domain-Policies: none",[259,498,500],{"id":499},"ci-security-scanning-sast-sca","CI Security Scanning (SAST + SCA)",[154,502,503,504,507,508],{},"Every push and pull request is scanned by 7 static security tools. ",[158,505,506],{},"All are blocking"," — failures prevent merge. Run all scans locally with: ",[335,509,510],{},"make security:ci",[512,513,515],"h4",{"id":514},"tool-inventory","Tool Inventory",[185,517,518,537],{},[188,519,520],{},[191,521,522,525,528,531,534],{},[194,523,524],{},"Tool",[194,526,527],{},"Type",[194,529,530],{},"What It Catches",[194,532,533],{},"CI Job",[194,535,536],{},"Blocking?",[201,538,539,561,581,606,626,646,666],{},[191,540,541,546,549,552,558],{},[206,542,543],{},[158,544,545],{},"gosec",[206,547,548],{},"SAST (Go)",[206,550,551],{},"SQL injection, hardcoded credentials, weak crypto, insecure TLS, SSRF",[206,553,554,557],{},[335,555,556],{},"lint:go"," (via golangci-lint)",[206,559,560],{},"✅ Yes",[191,562,563,568,571,574,579],{},[206,564,565],{},[158,566,567],{},"govulncheck",[206,569,570],{},"SCA (Go)",[206,572,573],{},"Known vulnerabilities in Go dependencies (call-graph analysis)",[206,575,576],{},[335,577,578],{},"security:govulncheck",[206,580,560],{},[191,582,583,588,591,594,599],{},[206,584,585],{},[158,586,587],{},"pnpm audit",[206,589,590],{},"SCA (Node.js)",[206,592,593],{},"Known vulnerabilities in npm\u002Fpnpm dependencies",[206,595,596],{},[335,597,598],{},"security:pnpm-audit",[206,600,601,602,374],{},"✅ Yes (see ",[172,603,605],{"href":604},"#temporary-workarounds--pnpm-audit-registry-410","Temporary Workarounds",[191,607,608,613,616,619,624],{},[206,609,610],{},[158,611,612],{},"Trivy (FS)",[206,614,615],{},"SCA (multi-lang)",[206,617,618],{},"Filesystem scan for Go module + Node.js dependency CVEs (HIGH\u002FCRITICAL)",[206,620,621],{},[335,622,623],{},"security:trivy",[206,625,560],{},[191,627,628,633,636,639,644],{},[206,629,630],{},[158,631,632],{},"Trivy (image)",[206,634,635],{},"Container scan",[206,637,638],{},"Alpine OS packages + binary CVEs in the Docker image",[206,640,641],{},[335,642,643],{},"security:trivy-image",[206,645,560],{},[191,647,648,653,656,659,664],{},[206,649,650],{},[158,651,652],{},"Gitleaks",[206,654,655],{},"Secret scanning",[206,657,658],{},"Accidentally committed API keys, passwords, tokens in git history",[206,660,661],{},[335,662,663],{},"security:gitleaks",[206,665,560],{},[191,667,668,673,676,679,684],{},[206,669,670],{},[158,671,672],{},"Semgrep",[206,674,675],{},"SAST (multi-lang)",[206,677,678],{},"The auto config rule set across all Go and Vue\u002FTypeScript source files, plus YAML, Dockerfile, and Bash",[206,680,681],{},[335,682,683],{},"security:semgrep",[206,685,560],{},[512,687,689,690,374],{"id":688},"gitleaks-configuration-gitleakstoml","Gitleaks Configuration (",[335,691,692],{},".gitleaks.toml",[154,694,695,696,699],{},"Gitleaks scans the entire git history for accidentally committed secrets. The following paths are allowlisted because they contain ",[158,697,698],{},"intentional example\u002Ftest credentials",", not real secrets:",[185,701,702,712],{},[188,703,704],{},[191,705,706,709],{},[194,707,708],{},"Allowlisted Path Pattern",[194,710,711],{},"Reason",[201,713,714,732,750],{},[191,715,716,721],{},[206,717,718],{},[335,719,720],{},"*_test.go",[206,722,723,724,727,728,731],{},"Go test files contain fake API keys (",[335,725,726],{},"valid-api-key-12345",", ",[335,729,730],{},"secret12345678",") and test JWT tokens used as fixtures in middleware, integration, and auth tests",[191,733,734,739],{},[206,735,736],{},[335,737,738],{},"docs\u002Freference\u002Fapi\u002F",[206,740,741,742,745,746,749],{},"API documentation contains example JWT tokens (",[335,743,744],{},"eyJhbGciOiJIUzI1NiIs...",") and example API keys (",[335,747,748],{},"ck_a1b2c3d4e5f6...",") in curl command examples",[191,751,752,757],{},[206,753,754],{},[335,755,756],{},"docs\u002Fplans\u002F",[206,758,759],{},"Plan documents may reference example credentials in design discussions",[154,761,762,768],{},[158,763,764,765,767],{},"All allowlisted patterns are documented in ",[335,766,692],{}," with rationale."," Gitleaks remains active for all production source code, configuration files, and scripts.",[512,770,772,773,776,777,374],{"id":771},"semgrep-configuration-semgrepignore-and-nosemgrep","Semgrep Configuration (",[335,774,775],{},".semgrepignore"," and ",[335,778,779],{},"nosemgrep",[154,781,782],{},"Semgrep scans all Go and Vue\u002FTypeScript source files (every file tracked by git except the marketing site). Test files, utility files, and all production code are scanned.",[154,784,785],{},[158,786,787,789],{},[335,788,775],{}," exclusion — 1 directory:",[185,791,792,804],{},[188,793,794],{},[191,795,796,799,802],{},[194,797,798],{},"Excluded Path",[194,800,801],{},"Files",[194,803,711],{},[201,805,806],{},[191,807,808,813,816],{},[206,809,810],{},[335,811,812],{},"site\u002F",[206,814,815],{},"37 files",[206,817,818,819,822,823,826,827,830],{},"Completely separate Nuxt Content marketing website with its own ",[335,820,821],{},"package.json",", dependencies, and deployment. Has no authentication, no database, no API. The ",[335,824,825],{},"ProseCode.vue"," component uses ",[335,828,829],{},"v-html"," to render Mermaid SVG diagrams generated by the Mermaid library from trusted markdown — not user input. Would require its own security review.",[154,832,833],{},[158,834,835],{},"Files skipped by Semgrep's built-in 1 MB size limit — 7 files:",[185,837,838,853],{},[188,839,840],{},[191,841,842,845,848,850],{},[194,843,844],{},"Skipped File",[194,846,847],{},"Size",[194,849,527],{},[194,851,852],{},"Security Relevance",[201,854,855],{},[191,856,857,863,866,869],{},[206,858,859,862],{},[335,860,861],{},"screenshots\u002F*.png"," (7 files)",[206,864,865],{},"1-5 MB each",[206,867,868],{},"PNG images",[206,870,871],{},"None — documentation screenshots, not parseable code",[154,873,874],{},[158,875,876,877,879],{},"Inline ",[335,878,779],{}," annotations — every suppressed finding with rationale:",[185,881,882,898],{},[188,883,884],{},[191,885,886,889,892,895],{},[194,887,888],{},"File",[194,890,891],{},"Line",[194,893,894],{},"Semgrep Rule",[194,896,897],{},"Rationale",[201,899,900,921,954,984,1005,1023,1049],{},[191,901,902,907,910,915],{},[206,903,904],{},[335,905,906],{},"backend\u002Finternal\u002Ftestutil\u002Ftestutil.go",[206,908,909],{},"247",[206,911,912],{},[335,913,914],{},"go.jwt-go.security.jwt.hardcoded-jwt-key",[206,916,917,920],{},[335,918,919],{},"TestJWTSecret"," is a test-only constant used to sign JWT tokens in unit tests. It is never used in production code.",[191,922,923,928,931,936],{},[206,924,925],{},[335,926,927],{},"backend\u002Froutes\u002Fauth.go",[206,929,930],{},"92",[206,932,933],{},[335,934,935],{},"go.lang.security.audit.net.cookie-missing-secure",[206,937,938,939,942,943,946,947,950,951,953],{},"The ",[335,940,941],{},"Secure"," flag is set to ",[335,944,945],{},"cfg.SecureCookies"," which evaluates to ",[335,948,949],{},"true"," when ",[335,952,389],{},". Semgrep cannot evaluate runtime configuration.",[191,955,956,960,963,971],{},[206,957,958],{},[335,959,927],{},[206,961,962],{},"106",[206,964,965,727,968],{},[335,966,967],{},"cookie-missing-httponly",[335,969,970],{},"cookie-missing-secure",[206,972,938,973,976,977,980,981,983],{},[335,974,975],{},"authenticated"," cookie is intentionally non-HttpOnly so the Vue SPA can detect auth state via JavaScript. It contains no secrets (just the string ",[335,978,979],{},"\"true\"","). The JWT cookie (which holds the actual token) IS HttpOnly. ",[335,982,941],{}," is conditional as above.",[191,985,986,991,994,998],{},[206,987,988],{},[335,989,990],{},"backend\u002Froutes\u002Fmiddleware_test.go",[206,992,993],{},"130",[206,995,996],{},[335,997,914],{},[206,999,1000,1001,1004],{},"Test intentionally signs a JWT with the wrong secret (",[335,1002,1003],{},"\"wrong-secret\"",") to verify the middleware rejects tokens signed with incorrect keys.",[191,1006,1007,1011,1014,1020],{},[206,1008,1009],{},[335,1010,990],{},[206,1012,1013],{},"233",[206,1015,1016,727,1018],{},[335,1017,967],{},[335,1019,970],{},[206,1021,1022],{},"Test request attaches a JWT cookie to simulate browser behavior. HttpOnly\u002FSecure are server-side attributes set when the cookie is issued by the login handler, not when the browser sends the cookie back.",[191,1024,1025,1030,1033,1038],{},[206,1026,1027],{},[335,1028,1029],{},"frontend\u002Fapp\u002Fcomposables\u002FuseEventStream.ts",[206,1031,1032],{},"214",[206,1034,1035],{},[335,1036,1037],{},"unsafe-formatstring",[206,1039,1040,1041,1044,1045,1048],{},"Template literal in ",[335,1042,1043],{},"console.warn"," uses ",[335,1046,1047],{},"eventType"," which is an internal SSE event type name from the server's event bus, not user-supplied input.",[191,1050,1051,1056,1059,1064],{},[206,1052,1053],{},[335,1054,1055],{},"frontend\u002Fapp\u002Fpages\u002Fhelp.vue",[206,1057,1058],{},"75",[206,1060,1061],{},[335,1062,1063],{},"avoid-v-html",[206,1065,1066,1067,1070],{},"HTML is pre-rendered at build time by the Vite announcements plugin from developer-authored markdown files in ",[335,1068,1069],{},"frontend\u002Fannouncements\u002F",". Content is never user-supplied; it ships with each release and is version-controlled.",[154,1072,1073],{},[158,1074,876,1075,1078],{},[335,1076,1077],{},"nolint"," annotations — every suppressed golangci-lint finding with rationale:",[185,1080,1081,1094],{},[188,1082,1083],{},[191,1084,1085,1087,1089,1092],{},[194,1086,888],{},[194,1088,891],{},[194,1090,1091],{},"Linter Rule",[194,1093,897],{},[201,1095,1096,1112,1131,1144,1163,1182,1204,1220,1234,1248,1261,1274,1286,1305,1335,1355,1368,1383,1398,1414,1427,1443,1461,1480,1498,1524],{},[191,1097,1098,1103,1106,1109],{},[206,1099,1100],{},[335,1101,1102],{},"backend\u002Finternal\u002Fcache\u002Fcache_test.go",[206,1104,1105],{},"185",[206,1107,1108],{},"errcheck",[206,1110,1111],{},"Deliberate: testing concurrent cache access, return value intentionally ignored",[191,1113,1114,1119,1122,1125],{},[206,1115,1116],{},[335,1117,1118],{},"backend\u002Finternal\u002Fconfig\u002Fconfig.go",[206,1120,1121],{},"85",[206,1123,1124],{},"gosec G706",[206,1126,1127,1128,1130],{},"Logging trusted env var header name (",[335,1129,353],{},"), not user input",[191,1132,1133,1137,1139,1141],{},[206,1134,1135],{},[335,1136,1118],{},[206,1138,930],{},[206,1140,1124],{},[206,1142,1143],{},"Security warning logs trusted env var header name, not user input",[191,1145,1146,1151,1154,1157],{},[206,1147,1148],{},[335,1149,1150],{},"backend\u002Finternal\u002Fdb\u002Fdb.go",[206,1152,1153],{},"182",[206,1155,1156],{},"gosec G201",[206,1158,1159,1162],{},[335,1160,1161],{},"fmt.Sprintf(\"PRAGMA table_info(%s)\", table)"," — table name is a hardcoded string from within the function, never user input",[191,1164,1165,1170,1173,1176],{},[206,1166,1167],{},[335,1168,1169],{},"backend\u002Finternal\u002Fengine\u002Fscore_test.go",[206,1171,1172],{},"29",[206,1174,1175],{},"unparam",[206,1177,1178,1181],{},[335,1179,1180],{},"value"," is always 10 in tests but the parameter documents intent for the helper function",[191,1183,1184,1189,1192,1194],{},[206,1185,1186],{},[335,1187,1188],{},"backend\u002Finternal\u002Fevents\u002Fsse_broadcaster.go",[206,1190,1191],{},"262",[206,1193,1108],{},[206,1195,1196,1199,1200,1203],{},[335,1197,1198],{},"json.Marshal"," of a ",[335,1201,1202],{},"string"," value cannot fail",[191,1205,1206,1211,1214,1217],{},[206,1207,1208],{},[335,1209,1210],{},"backend\u002Finternal\u002Fintegrations\u002Farr_helpers.go",[206,1212,1213],{},"246",[206,1215,1216],{},"gosec G704",[206,1218,1219],{},"URL is from admin-configured integration settings, not user-tainted",[191,1221,1222,1227,1230,1232],{},[206,1223,1224],{},[335,1225,1226],{},"backend\u002Finternal\u002Fintegrations\u002Fhttpclient.go",[206,1228,1229],{},"81",[206,1231,1216],{},[206,1233,1219],{},[191,1235,1236,1240,1243,1245],{},[206,1237,1238],{},[335,1239,1226],{},[206,1241,1242],{},"89",[206,1244,1124],{},[206,1246,1247],{},"Sanitized URL, HTTP status code, and duration are safe to log",[191,1249,1250,1254,1257,1259],{},[206,1251,1252],{},[335,1253,1226],{},[206,1255,1256],{},"137",[206,1258,1216],{},[206,1260,1219],{},[191,1262,1263,1267,1270,1272],{},[206,1264,1265],{},[335,1266,1226],{},[206,1268,1269],{},"188",[206,1271,1216],{},[206,1273,1219],{},[191,1275,1276,1280,1282,1284],{},[206,1277,1278],{},[335,1279,1226],{},[206,1281,1013],{},[206,1283,1216],{},[206,1285,1219],{},[191,1287,1288,1293,1296,1299],{},[206,1289,1290],{},[335,1291,1292],{},"backend\u002Finternal\u002Fintegrations\u002Fjellystat_test.go",[206,1294,1295],{},"11",[206,1297,1298],{},"gosec G101",[206,1300,1301,1304],{},[335,1302,1303],{},"testJellystatAPIKey"," is a test fixture constant, not a real credential",[191,1306,1307,1312,1315,1318],{},[206,1308,1309],{},[335,1310,1311],{},"backend\u002Finternal\u002Fintegrations\u002Fplex.go",[206,1313,1314],{},"243",[206,1316,1317],{},"exhaustive",[206,1319,1320,1321,727,1324,727,1327,1330,1331,1334],{},"Plex API only returns ",[335,1322,1323],{},"movie",[335,1325,1326],{},"show",[335,1328,1329],{},"season",", and ",[335,1332,1333],{},"episode"," media types",[191,1336,1337,1342,1345,1347],{},[206,1338,1339],{},[335,1340,1341],{},"backend\u002Finternal\u002Fintegrations\u002Fsonarr.go",[206,1343,1344],{},"164",[206,1346,1317],{},[206,1348,1349,1350,776,1352,1354],{},"Sonarr integration only handles ",[335,1351,1326],{},[335,1353,1329],{}," types",[191,1356,1357,1361,1364,1366],{},[206,1358,1359],{},[335,1360,1341],{},[206,1362,1363],{},"212",[206,1365,1216],{},[206,1367,1219],{},[191,1369,1370,1375,1378,1380],{},[206,1371,1372],{},[335,1373,1374],{},"backend\u002Finternal\u002Fnotifications\u002Fhttpclient.go",[206,1376,1377],{},"52",[206,1379,545],{},[206,1381,1382],{},"URL is from admin-configured webhook notification settings",[191,1384,1385,1390,1393,1395],{},[206,1386,1387],{},[335,1388,1389],{},"backend\u002Finternal\u002Fservices\u002Fauth.go",[206,1391,1392],{},"213",[206,1394,1124],{},[206,1396,1397],{},"Username is from a trusted reverse proxy header, not user-supplied",[191,1399,1400,1405,1408,1411],{},[206,1401,1402],{},[335,1403,1404],{},"backend\u002Finternal\u002Fservices\u002Fnotification_dispatch_test.go",[206,1406,1407],{},"327",[206,1409,1410],{},"dupl",[206,1412,1413],{},"Test structure intentionally similar to related dispatch tests",[191,1415,1416,1420,1423,1425],{},[206,1417,1418],{},[335,1419,1404],{},[206,1421,1422],{},"348",[206,1424,1410],{},[206,1426,1413],{},[191,1428,1429,1434,1437,1440],{},[206,1430,1431],{},[335,1432,1433],{},"backend\u002Finternal\u002Fservices\u002Fposter_overlay.go",[206,1435,1436],{},"419",[206,1438,1439],{},"gosec G107",[206,1441,1442],{},"URL is from *arr metadata (PosterURL → TMDb CDN), not user input",[191,1444,1445,1450,1453,1455],{},[206,1446,1447],{},[335,1448,1449],{},"backend\u002Finternal\u002Fservices\u002Fschema.go",[206,1451,1452],{},"218",[206,1454,1156],{},[206,1456,1457,1460],{},[335,1458,1459],{},"fmt.Sprintf(\"PRAGMA table_info(%s)\", tableName)"," — table name is a hardcoded string from within the service, never user input",[191,1462,1463,1468,1471,1473],{},[206,1464,1465],{},[335,1466,1467],{},"backend\u002Finternal\u002Fservices\u002Fversion.go",[206,1469,1470],{},"153",[206,1472,545],{},[206,1474,1475,1476,1479],{},"URL is set at construction time (",[335,1477,1478],{},"DefaultGitHubReleasesURL","), not user-tainted",[191,1481,1482,1486,1488,1490],{},[206,1483,1484],{},[335,1485,927],{},[206,1487,930],{},[206,1489,545],{},[206,1491,1492,1494,1495,1497],{},[335,1493,941],{}," flag conditionally set via ",[335,1496,945],{}," — not all self-hosted environments use HTTPS. Also suppresses Semgrep (see nosemgrep table above)",[191,1499,1500,1504,1506,1508],{},[206,1501,1502],{},[335,1503,927],{},[206,1505,962],{},[206,1507,545],{},[206,1509,1510,1513,1514,1517,1518,1520,1521,1523],{},[335,1511,1512],{},"HttpOnly"," intentionally ",[335,1515,1516],{},"false",": cookie contains no secrets (just ",[335,1519,979],{},"), allows SPA auth state detection. ",[335,1522,941],{}," conditional as above. Also suppresses Semgrep (see nosemgrep table above)",[191,1525,1526,1531,1534,1536],{},[206,1527,1528],{},[335,1529,1530],{},"backend\u002Froutes\u002Fsecurity_test.go",[206,1532,1533],{},"194",[206,1535,1298],{},[206,1537,1538],{},"Test fixture API key in integration creation request body, not a real credential",[154,1540,1541,1544],{},[158,1542,1543],{},"Semgrep partial-parse warnings"," (files that are scanned but where Semgrep's parser can't fully parse certain syntax):",[185,1546,1547,1561],{},[188,1548,1549],{},[191,1550,1551,1553,1556,1559],{},[194,1552,888],{},[194,1554,1555],{},"Parsed",[194,1557,1558],{},"Unparsed Lines",[194,1560,711],{},[201,1562,1563,1590,1606,1622],{},[191,1564,1565,1570,1573,1580],{},[206,1566,1567],{},[335,1568,1569],{},"Dockerfile",[206,1571,1572],{},"~45%",[206,1574,1575,1576,1579],{},"Lines 29-62 (multi-stage build args + ",[335,1577,1578],{},"RUN"," commands)",[206,1581,1582,1583,1586,1587,1589],{},"Semgrep's Dockerfile parser has limited support for complex ",[335,1584,1585],{},"ARG","\u002F",[335,1588,1578],{}," syntax. The important security-relevant parts (base images, package installs) are parsed.",[191,1591,1592,1597,1600,1603],{},[206,1593,1594],{},[335,1595,1596],{},"frontend\u002Fnuxt.config.ts",[206,1598,1599],{},"~99.5%",[206,1601,1602],{},"\u003C1% of lines",[206,1604,1605],{},"Complex TypeScript config structure; nearly fully parsed.",[191,1607,1608,1613,1616,1619],{},[206,1609,1610],{},[335,1611,1612],{},"scripts\u002Fdocker-build.sh",[206,1614,1615],{},"~97%",[206,1617,1618],{},"~3%",[206,1620,1621],{},"Shell script parsing limitation.",[191,1623,1624,1629,1632,1635],{},[206,1625,1626],{},[335,1627,1628],{},"scripts\u002Fdocker-mirror.sh",[206,1630,1631],{},"~94%",[206,1633,1634],{},"~6%",[206,1636,1621],{},[259,1638,1640],{"id":1639},"dynamic-application-security-testing-dast","Dynamic Application Security Testing (DAST)",[154,1642,1643,1644,1649],{},"In addition to static analysis, Capacitarr is tested with ",[172,1645,1648],{"href":1646,"rel":1647},"https:\u002F\u002Fwww.zaproxy.org\u002F",[240],"OWASP ZAP"," — the industry-standard open-source DAST tool. ZAP makes real HTTP requests with attack payloads against a running instance, testing for the OWASP Top 10 and 50+ additional vulnerability categories.",[154,1651,1652,1653],{},"Run locally: ",[335,1654,1655],{},"make build && make security:zap",[154,1657,1658,1661,1662],{},[158,1659,1660],{},"Latest baseline (2026-04-06, pre-release scan for v3.1.0):"," 119 rules tested (53 active + 66 passive), ",[158,1663,1664],{},"118 PASS, 0 FAIL, 1 WARN",[185,1666,1667,1680],{},[188,1668,1669],{},[191,1670,1671,1674,1677],{},[194,1672,1673],{},"Category",[194,1675,1676],{},"Tests",[194,1678,1679],{},"Result",[201,1681,1682,1693,1703,1712,1721,1730,1740,1750,1760,1770,1780,1789,1798,1808,1818],{},[191,1683,1684,1687,1690],{},[206,1685,1686],{},"SQL Injection (6 database engines)",[206,1688,1689],{},"6",[206,1691,1692],{},"✅ All PASS",[191,1694,1695,1698,1701],{},[206,1696,1697],{},"Cross-Site Scripting (Reflected, Persistent, DOM)",[206,1699,1700],{},"5",[206,1702,1692],{},[191,1704,1705,1708,1710],{},[206,1706,1707],{},"Remote Code\u002FCommand Execution",[206,1709,1700],{},[206,1711,1692],{},[191,1713,1714,1717,1719],{},[206,1715,1716],{},"Server-Side Attacks (XXE, SSTI, SSRF, SOAP)",[206,1718,1689],{},[206,1720,1692],{},[191,1722,1723,1726,1728],{},[206,1724,1725],{},"Path Traversal & File Disclosure",[206,1727,1700],{},[206,1729,1692],{},[191,1731,1732,1735,1738],{},[206,1733,1734],{},"Known CVEs (Log4Shell, Spring4Shell)",[206,1736,1737],{},"4",[206,1739,1692],{},[191,1741,1742,1745,1748],{},[206,1743,1744],{},"Infrastructure (Buffer Overflow, CRLF, Cloud Metadata)",[206,1746,1747],{},"16",[206,1749,1692],{},[191,1751,1752,1755,1758],{},[206,1753,1754],{},"Authentication & Session",[206,1756,1757],{},"3",[206,1759,1692],{},[191,1761,1762,1765,1768],{},[206,1763,1764],{},"Security Headers & Configuration",[206,1766,1767],{},"17",[206,1769,1692],{},[191,1771,1772,1775,1778],{},[206,1773,1774],{},"Information Disclosure",[206,1776,1777],{},"12",[206,1779,1692],{},[191,1781,1782,1785,1787],{},[206,1783,1784],{},"Transport Security",[206,1786,1700],{},[206,1788,1692],{},[191,1790,1791,1794,1796],{},[206,1792,1793],{},"Passive Authentication & Session",[206,1795,1700],{},[206,1797,1692],{},[191,1799,1800,1803,1806],{},[206,1801,1802],{},"Known Vulnerabilities & Miscellaneous",[206,1804,1805],{},"18",[206,1807,1692],{},[191,1809,1810,1813,1816],{},[206,1811,1812],{},"Cross-Site & Redirect Attacks (Passive)",[206,1814,1815],{},"8",[206,1817,1692],{},[191,1819,1820,1823,1826],{},[206,1821,1822],{},"Unexpected Content-Type (SPA fallback)",[206,1824,1825],{},"1",[206,1827,1828],{},"⚠️ WARN (expected)",[154,1830,1831,1832,1837,1838,727,1841,727,1844,727,1847,1850],{},"The full test-by-test breakdown with rule IDs is in ",[172,1833,1834],{"href":141},[335,1835,1836],{},"docs\u002Fsecurity\u002Fzap-baseline-20260406.md",". Previous baselines: ",[172,1839,1840],{"href":138},"2026-03-24",[172,1842,1843],{"href":135},"2026-03-23",[172,1845,1846],{"href":132},"2026-03-16",[172,1848,1849],{"href":129},"2026-03-10",".",[154,1852,1853,1856,1857,1860],{},[158,1854,1855],{},"Testing cadence:"," Run DAST scanning (",[335,1858,1859],{},"make security:zap",") before each release, after significant code changes affecting HTTP handlers or authentication, and periodically as part of routine security hygiene. The baseline should be updated in this document after each scan.",[259,1862,1864],{"id":1863},"temporary-workarounds-pnpm-audit-registry-410","Temporary Workarounds — pnpm audit Registry 410",[154,1866,1867,1868,776,1871,1874,1875,1877,1878,1850],{},"As of 2026-04-15, the npm registry retired both legacy audit endpoints (",[335,1869,1870],{},"\u002F-\u002Fnpm\u002Fv1\u002Fsecurity\u002Faudits",[335,1872,1873],{},"\u002F-\u002Fnpm\u002Fv1\u002Fsecurity\u002Faudits\u002Fquick","), returning HTTP 410 Gone. This breaks ",[335,1876,587],{}," on all pnpm 10.x versions and 11.0.0-rc.0. The upstream fix is tracked in ",[172,1879,1882],{"href":1880,"rel":1881},"https:\u002F\u002Fgithub.com\u002Fpnpm\u002Fpnpm\u002Fissues\u002F11265",[240],"pnpm\u002Fpnpm#11265",[154,1884,1885,1888,1889,1892,1893,1895,1896,1899,1900,1903,1904,1907],{},[158,1886,1887],{},"Workaround applied:"," The ",[335,1890,1891],{},"--ignore-registry-errors"," flag has been added to ",[335,1894,587],{}," in both the Makefile (",[335,1897,1898],{},"security:ci"," target) and ",[335,1901,1902],{},".github\u002Fworkflows\u002Fci.yml"," (",[335,1905,1906],{},"security-pnpm-audit"," job). This prevents the 410 response from failing the CI pipeline.",[154,1909,1910,1913,1914,1916,1917,1919,1920,1903,1923,1925,1926,1929],{},[158,1911,1912],{},"Impact on security posture:"," Minimal. The ",[335,1915,1891],{}," flag means ",[335,1918,587],{}," will pass even if the registry is unreachable, but ",[158,1921,1922],{},"Trivy filesystem scanning",[335,1924,623],{},") independently scans all Node.js dependencies for HIGH\u002FCRITICAL CVEs using the same vulnerability databases. The ",[335,1927,1928],{},"pnpm.overrides"," policy (see below) continues to enforce patched dependency versions at install time.",[154,1931,1932,1935,1936,1939,1940,1942],{},[158,1933,1934],{},"When to remove:"," Once pnpm ships a release that uses the new bulk advisory endpoint (",[335,1937,1938],{},"\u002F-\u002Fnpm\u002Fv1\u002Fsecurity\u002Fadvisories\u002Fbulk","), remove the ",[335,1941,1891],{}," flag from the Makefile and CI workflow, and delete this section.",[259,1944,1946,1947,374],{"id":1945},"dependency-override-policy-pnpmoverrides","Dependency Override Policy (",[335,1948,1928],{},[154,1950,1951,1952,1954,1955,1958],{},"When transitive npm dependencies have known vulnerabilities but the upstream parent package (e.g., Nuxt, ESLint) has not yet released an update with a patched version, we use ",[335,1953,1928],{}," in ",[335,1956,1957],{},"frontend\u002Fpackage.json"," to force the patched version. This ensures:",[264,1960,1961,1970,1973],{},[230,1962,938,1963,1965,1966,1969],{},[335,1964,598],{}," CI job continues to enforce ",[158,1967,1968],{},"zero known vulnerabilities"," as a blocking gate",[230,1971,1972],{},"Shipped Docker images contain patched dependency versions, not just silenced findings",[230,1974,1975,1976,1979,1980,1983],{},"The security posture is not weakened by ",[335,1977,1978],{},"allow_failure"," or audit ",[335,1981,1982],{},"--ignore"," flags",[154,1985,1986,1989],{},[158,1987,1988],{},"Current overrides"," (as of 2026-04-05):",[185,1991,1992,2011],{},[188,1993,1994],{},[191,1995,1996,1999,2002,2005,2008],{},[194,1997,1998],{},"Package",[194,2000,2001],{},"Override",[194,2003,2004],{},"Advisory",[194,2006,2007],{},"Severity",[194,2009,2010],{},"Upstream Dep",[201,2012,2013,2056,2096,2122,2154,2180,2207,2233,2259,2291,2322,2362,2388,2414,2445,2486,2517,2544,2577],{},[191,2014,2015,2020,2033,2045,2048],{},[206,2016,2017],{},[335,2018,2019],{},"minimatch",[206,2021,2022,2025,2026,2025,2029,2032],{},[335,2023,2024],{},">=5.1.8"," \u002F ",[335,2027,2028],{},">=9.0.7",[335,2030,2031],{},">=10.2.3"," (per-major)",[206,2034,2035,727,2040],{},[172,2036,2039],{"href":2037,"rel":2038},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-7r86-cg39-jmmj",[240],"GHSA-7r86-cg39-jmmj",[172,2041,2044],{"href":2042,"rel":2043},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-23c5-xmqv-rm74",[240],"GHSA-23c5-xmqv-rm74",[206,2046,2047],{},"High",[206,2049,2050,727,2053],{},[335,2051,2052],{},"nuxt > nitropack > @vercel\u002Fnft > glob",[335,2054,2055],{},"@nuxt\u002Feslint",[191,2057,2058,2063,2073,2085,2088],{},[206,2059,2060],{},[335,2061,2062],{},"picomatch",[206,2064,2065,2068,2069,2072],{},[335,2066,2067],{},"2.3.2"," (for \u003C2.3.2) \u002F ",[335,2070,2071],{},"4.0.4"," (for >=4.0.0 \u003C4.0.4)",[206,2074,2075,727,2080],{},[172,2076,2079],{"href":2077,"rel":2078},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-c2c7-rcm5-vvqj",[240],"GHSA-c2c7-rcm5-vvqj",[172,2081,2084],{"href":2082,"rel":2083},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-3v7f-55p6-f55p",[240],"GHSA-3v7f-55p6-f55p",[206,2086,2087],{},"High \u002F Moderate",[206,2089,2090,727,2093],{},[335,2091,2092],{},"@vite-pwa\u002Fnuxt > workbox-build > @rollup\u002Fpluginutils",[335,2094,2095],{},"nuxt > unstorage > anymatch",[191,2097,2098,2103,2108,2115,2117],{},[206,2099,2100],{},[335,2101,2102],{},"rollup",[206,2104,2105],{},[335,2106,2107],{},">=4.59.0",[206,2109,2110],{},[172,2111,2114],{"href":2112,"rel":2113},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-mw96-cpmx-2vgc",[240],"GHSA-mw96-cpmx-2vgc",[206,2116,2047],{},[206,2118,2119],{},[335,2120,2121],{},"nuxt > vite",[191,2123,2124,2129,2134,2146,2149],{},[206,2125,2126],{},[335,2127,2128],{},"serialize-javascript",[206,2130,2131],{},[335,2132,2133],{},">=7.0.5",[206,2135,2136,727,2141],{},[172,2137,2140],{"href":2138,"rel":2139},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-5c6j-r48x-rmvq",[240],"GHSA-5c6j-r48x-rmvq",[172,2142,2145],{"href":2143,"rel":2144},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-qj8w-gfj5-8c6v",[240],"GHSA-qj8w-gfj5-8c6v",[206,2147,2148],{},"Moderate",[206,2150,2151],{},[335,2152,2153],{},"@vite-pwa\u002Fnuxt > vite-plugin-pwa > workbox-build > @rollup\u002Fplugin-terser",[191,2155,2156,2161,2166,2173,2175],{},[206,2157,2158],{},[335,2159,2160],{},"svgo",[206,2162,2163],{},[335,2164,2165],{},">=4.0.1",[206,2167,2168],{},[172,2169,2172],{"href":2170,"rel":2171},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-xpqw-6gx7-v673",[240],"GHSA-xpqw-6gx7-v673",[206,2174,2047],{},[206,2176,2177],{},[335,2178,2179],{},"nuxt > @nuxt\u002Fvite-builder > cssnano > postcss-svgo",[191,2181,2182,2187,2192,2199,2202],{},[206,2183,2184],{},[335,2185,2186],{},"simple-git",[206,2188,2189],{},[335,2190,2191],{},">=3.32.3",[206,2193,2194],{},[172,2195,2198],{"href":2196,"rel":2197},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-r275-fr43-pm7q",[240],"GHSA-r275-fr43-pm7q",[206,2200,2201],{},"Critical",[206,2203,2204],{},[335,2205,2206],{},"nuxt > @nuxt\u002Fdevtools",[191,2208,2209,2214,2219,2226,2228],{},[206,2210,2211],{},[335,2212,2213],{},"tar",[206,2215,2216],{},[335,2217,2218],{},">=7.5.11",[206,2220,2221],{},[172,2222,2225],{"href":2223,"rel":2224},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-9ppj-qmqm-q256",[240],"GHSA-9ppj-qmqm-q256",[206,2227,2047],{},[206,2229,2230],{},[335,2231,2232],{},"nuxt > nitropack > @vercel\u002Fnft > @mapbox\u002Fnode-pre-gyp",[191,2234,2235,2240,2245,2252,2254],{},[206,2236,2237],{},[335,2238,2239],{},"flatted",[206,2241,2242],{},[335,2243,2244],{},">=3.4.2",[206,2246,2247],{},[172,2248,2251],{"href":2249,"rel":2250},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-25h7-pfq9-p65f",[240],"GHSA-25h7-pfq9-p65f",[206,2253,2047],{},[206,2255,2256],{},[335,2257,2258],{},"eslint > file-entry-cache > flat-cache",[191,2260,2261,2266,2271,2283,2286],{},[206,2262,2263],{},[335,2264,2265],{},"devalue",[206,2267,2268],{},[335,2269,2270],{},">=5.6.4",[206,2272,2273,727,2278],{},[172,2274,2277],{"href":2275,"rel":2276},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-cfw5-2vxh-hr84",[240],"GHSA-cfw5-2vxh-hr84",[172,2279,2282],{"href":2280,"rel":2281},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-mwv9-gp5h-frr4",[240],"GHSA-mwv9-gp5h-frr4",[206,2284,2285],{},"Moderate \u002F Low",[206,2287,2288],{},[335,2289,2290],{},"nuxt",[191,2292,2293,2298,2303,2315,2317],{},[206,2294,2295],{},[335,2296,2297],{},"unhead",[206,2299,2300],{},[335,2301,2302],{},">=2.1.11",[206,2304,2305,727,2310],{},[172,2306,2309],{"href":2307,"rel":2308},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-g5xx-pwrp-g3fv",[240],"GHSA-g5xx-pwrp-g3fv",[172,2311,2314],{"href":2312,"rel":2313},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-5339-hvwr-7582",[240],"GHSA-5339-hvwr-7582",[206,2316,2285],{},[206,2318,2319],{},[335,2320,2321],{},"nuxt > @unhead\u002Fvue",[191,2323,2324,2328,2333,2355,2357],{},[206,2325,2326],{},[335,2327,259],{},[206,2329,2330],{},[335,2331,2332],{},">=1.15.9",[206,2334,2335,727,2340,727,2345,727,2350],{},[172,2336,2339],{"href":2337,"rel":2338},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-22cc-p3c6-wpvm",[240],"GHSA-22cc-p3c6-wpvm",[172,2341,2344],{"href":2342,"rel":2343},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-wr4h-v87w-p3r7",[240],"GHSA-wr4h-v87w-p3r7",[172,2346,2349],{"href":2347,"rel":2348},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-72gr-qfp7-vwhw",[240],"GHSA-72gr-qfp7-vwhw",[172,2351,2354],{"href":2352,"rel":2353},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-4hxc-9384-m385",[240],"GHSA-4hxc-9384-m385",[206,2356,2087],{},[206,2358,2359],{},[335,2360,2361],{},"nuxt > nitropack > h3",[191,2363,2364,2369,2374,2381,2383],{},[206,2365,2366],{},[335,2367,2368],{},"yaml",[206,2370,2371],{},[335,2372,2373],{},">=2.8.3",[206,2375,2376],{},[172,2377,2380],{"href":2378,"rel":2379},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-48c2-rrv3-qjmp",[240],"GHSA-48c2-rrv3-qjmp",[206,2382,2148],{},[206,2384,2385],{},[335,2386,2387],{},"@nuxt\u002Feslint > @nuxt\u002Fdevtools-kit > vite > yaml",[191,2389,2390,2395,2400,2407,2409],{},[206,2391,2392],{},[335,2393,2394],{},"srvx",[206,2396,2397],{},[335,2398,2399],{},">=0.11.13",[206,2401,2402],{},[172,2403,2406],{"href":2404,"rel":2405},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-p36q-q72m-gchr",[240],"GHSA-p36q-q72m-gchr",[206,2408,2148],{},[206,2410,2411],{},[335,2412,2413],{},"nuxt > nitropack > srvx",[191,2415,2416,2421,2431,2438,2440],{},[206,2417,2418],{},[335,2419,2420],{},"brace-expansion",[206,2422,2423,2426,2427,2430],{},[335,2424,2425],{},">=5.0.5"," (for \u003C5.0.5) \u002F ",[335,2428,2429],{},">=2.0.3"," (for >=2.0.0 \u003C2.0.3)",[206,2432,2433],{},[172,2434,2437],{"href":2435,"rel":2436},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-f886-m6hf-6m8v",[240],"GHSA-f886-m6hf-6m8v",[206,2439,2148],{},[206,2441,2442],{},[335,2443,2444],{},"nuxt > nitropack > @vercel\u002Fnft > glob > brace-expansion",[191,2446,2447,2452,2457,2479,2481],{},[206,2448,2449],{},[335,2450,2451],{},"node-forge",[206,2453,2454],{},[335,2455,2456],{},">=1.4.0",[206,2458,2459,727,2464,727,2469,727,2474],{},[172,2460,2463],{"href":2461,"rel":2462},"https:\u002F\u002Fwww.cve.org\u002FCVERecord?id=CVE-2026-33891",[240],"CVE-2026-33891",[172,2465,2468],{"href":2466,"rel":2467},"https:\u002F\u002Fwww.cve.org\u002FCVERecord?id=CVE-2026-33894",[240],"CVE-2026-33894",[172,2470,2473],{"href":2471,"rel":2472},"https:\u002F\u002Fwww.cve.org\u002FCVERecord?id=CVE-2026-33895",[240],"CVE-2026-33895",[172,2475,2478],{"href":2476,"rel":2477},"https:\u002F\u002Fwww.cve.org\u002FCVERecord?id=CVE-2026-33896",[240],"CVE-2026-33896",[206,2480,2047],{},[206,2482,2483],{},[335,2484,2485],{},"nuxt > @nuxt\u002Fcli > listhen > node-forge",[191,2487,2488,2493,2498,2510,2512],{},[206,2489,2490],{},[335,2491,2492],{},"lodash",[206,2494,2495],{},[335,2496,2497],{},">=4.18.0",[206,2499,2500,727,2505],{},[172,2501,2504],{"href":2502,"rel":2503},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-r5fr-rjxr-66jc",[240],"GHSA-r5fr-rjxr-66jc",[172,2506,2509],{"href":2507,"rel":2508},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-f23m-r3pf-42rh",[240],"GHSA-f23m-r3pf-42rh",[206,2511,2087],{},[206,2513,2514],{},[335,2515,2516],{},"@vite-pwa\u002Fnuxt > workbox-build > archiver-utils",[191,2518,2519,2524,2528,2536,2538],{},[206,2520,2521],{},[335,2522,2523],{},"lodash-es",[206,2525,2526],{},[335,2527,2497],{},[206,2529,2530,727,2533],{},[172,2531,2504],{"href":2502,"rel":2532},[240],[172,2534,2509],{"href":2507,"rel":2535},[240],[206,2537,2087],{},[206,2539,2540,2543],{},[335,2541,2542],{},"@vite-pwa\u002Fnuxt > workbox-build"," (ESM variant of lodash)",[191,2545,2546,2551,2556,2563,2565],{},[206,2547,2548],{},[335,2549,2550],{},"defu",[206,2552,2553],{},[335,2554,2555],{},">=6.1.5",[206,2557,2558],{},[172,2559,2562],{"href":2560,"rel":2561},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-737v-mqg7-c878",[240],"GHSA-737v-mqg7-c878",[206,2564,2047],{},[206,2566,2567,727,2570,727,2573,2576],{},[335,2568,2569],{},"nuxt > nitropack",[335,2571,2572],{},"nuxt > c12",[335,2574,2575],{},"nuxt > @nuxt\u002Fkit"," (UnJS config defaults utility)",[191,2578,2579,2584,2589,2596,2598],{},[206,2580,2581],{},[335,2582,2583],{},"socket.io-parser",[206,2585,2586],{},[335,2587,2588],{},">=4.2.6",[206,2590,2591],{},[172,2592,2595],{"href":2593,"rel":2594},"https:\u002F\u002Fgithub.com\u002Fadvisories\u002FGHSA-677m-j7p3-52f9",[240],"GHSA-677m-j7p3-52f9",[206,2597,2047],{},[206,2599,2600],{},[335,2601,2602],{},"nuxt > @nuxt\u002Fdevtools > socket.io",[154,2604,2605,2608,2609,2611],{},[158,2606,2607],{},"When to remove overrides:"," After upstream packages release versions that natively depend on the patched versions, ",[335,2610,587],{}," will pass without overrides. At that point, remove the override entries and verify. Overrides that remain after upstream updates are harmless (they match or are lower than the naturally resolved version) but should be cleaned up for hygiene.",[154,2613,2614,2617,2618,2620,2621,2624,2625,2627],{},[158,2615,2616],{},"When adding new overrides:"," Add the override to ",[335,2619,1957],{},", update this table, and include the advisory URL in the commit message. Run ",[335,2622,2623],{},"pnpm install"," to regenerate the lockfile and ",[335,2626,587],{}," to verify.",[259,2629,2631],{"id":2630},"gosec-g117-json-secret-field-policy","Gosec G117 — JSON Secret Field Policy",[154,2633,2634,2635,2640,2641,727,2644,727,2647,727,2650,2653,2654,2657],{},"Gosec rule ",[172,2636,2639],{"href":2637,"rel":2638},"https:\u002F\u002Fsecurego.io\u002Fdocs\u002Frules\u002Fg117.html",[240],"G117"," flags exported struct fields whose JSON key names match secret patterns (",[335,2642,2643],{},"password",[335,2645,2646],{},"apiKey",[335,2648,2649],{},"token",[335,2651,2652],{},"secret","). The rule aims to prevent accidental serialization of sensitive data — for example, a secret leaking into logs when a struct is formatted with ",[335,2655,2656],{},"%+v"," or marshaled to JSON in an error response.",[154,2659,2660],{},[158,2661,2662],{},"How Capacitarr handles this:",[227,2664,2665,2713],{},[230,2666,2667,2674,2675,2702,2705,2706,2709,2710,2712],{},[158,2668,2669,2670,2673],{},"All internal structs use ",[335,2671,2672],{},"json:\"-\""," tags on secret fields."," This includes:",[264,2676,2677,2683,2692],{},[230,2678,2679,2682],{},[335,2680,2681],{},"config.Config.JWTSecret"," — application configuration",[230,2684,2685,776,2688,2691],{},[335,2686,2687],{},"db.AuthConfig.Password",[335,2689,2690],{},"db.AuthConfig.APIKey"," — user credentials",[230,2693,2694,2695,727,2698,2701],{},"All integration client structs (",[335,2696,2697],{},"EmbyClient.APIKey",[335,2699,2700],{},"PlexClient.Token",", etc.)",[2703,2704],"br",{},"These fields are ",[158,2707,2708],{},"never"," serialized to JSON. The ",[335,2711,2672],{}," tag is the correct structural fix for G117 and prevents accidental exposure regardless of how the struct is used.",[230,2714,2715,2718,2719,2756,2758,2759,2762,2763,2766,2767,2769],{},[158,2716,2717],{},"Three files are excluded from G117 via per-file linter exclusion."," These files define structs where secret-pattern JSON keys are part of the REST API contract:",[264,2720,2721,2738,2750],{},[230,2722,2723,2726,2727,1903,2730,2733,2734,2737],{},[335,2724,2725],{},"internal\u002Fdb\u002Fmodels.go"," — ",[335,2728,2729],{},"IntegrationConfig.APIKey",[335,2731,2732],{},"json:\"apiKey\"",") is the user-configured integration credential. It is ",[158,2735,2736],{},"masked"," before inclusion in any API response; only the last 4 characters are visible.",[230,2739,2740,2726,2743,1903,2746,2749],{},[335,2741,2742],{},"routes\u002Fauth.go",[335,2744,2745],{},"LoginRequest.Password",[335,2747,2748],{},"json:\"password\"",") accepts the user's password for authentication. This struct is decode-only and is never JSON-encoded.",[230,2751,2752,2755],{},[335,2753,2754],{},"routes\u002Fintegrations.go"," — Connection test request accepts an API key. Decode-only, never JSON-encoded.",[2703,2757],{},"These exclusions are defined in ",[335,2760,2761],{},"backend\u002F.golangci.yml"," using path+text matching. G117 remains ",[158,2764,2765],{},"active for all other files"," — any new struct with a secret-pattern JSON key will be flagged and must be addressed with either a ",[335,2768,2672],{}," tag or an explicit addition to the exclusion list after security review.",[154,2771,2772,2775],{},[158,2773,2774],{},"Why not a global G117 exclusion?"," A global exclusion would silently pass any future struct that accidentally exposes a secret field in JSON. The per-file approach ensures that each exemption is explicitly documented and the rest of the codebase remains protected.",[259,2777,2779],{"id":2778},"container-hardening","Container Hardening",[154,2781,2782],{},"The official Docker image uses a hardened Alpine runtime:",[264,2784,2785,2791,2811,2825,2853,2862],{},[230,2786,2787,2790],{},[158,2788,2789],{},"Alpine digest pinned:"," The runtime base image is pinned to a specific SHA-256 digest for reproducible, auditable builds. The digest is updated periodically (or via Renovate Bot) to pick up security patches",[230,2792,2793,2796,2797,2800,2801,727,2804,727,2807,2810],{},[158,2794,2795],{},"Package manager removed:"," ",[335,2798,2799],{},"apk"," is deleted after installing runtime dependencies (",[335,2802,2803],{},"ca-certificates",[335,2805,2806],{},"tzdata",[335,2808,2809],{},"su-exec","). An attacker with code execution cannot install additional tools",[230,2812,2813,2816,2817,2820,2821,2824],{},[158,2814,2815],{},"No curl\u002Fwget packages:"," Healthchecks use busybox ",[335,2818,2819],{},"wget"," (built into Alpine's busybox), eliminating the ",[335,2822,2823],{},"curl"," package from the attack surface",[230,2826,2827,2796,2830,2833,2834,2837,2838,2841,2842,2845,2846,1586,2849,2852],{},[158,2828,2829],{},"Capabilities dropped:",[335,2831,2832],{},"cap_drop: ALL"," removes all Linux capabilities, then ",[335,2835,2836],{},"cap_add"," restores only the 4 needed by the PUID\u002FPGID entrypoint: ",[335,2839,2840],{},"CHOWN"," (chown \u002Fconfig), ",[335,2843,2844],{},"DAC_OVERRIDE"," (create user in \u002Fetc\u002Fpasswd), ",[335,2847,2848],{},"SETUID",[335,2850,2851],{},"SETGID"," (su-exec drops to PUID:PGID). The Go binary itself needs zero capabilities",[230,2854,2855,2796,2858,2861],{},[158,2856,2857],{},"No privilege escalation:",[335,2859,2860],{},"no-new-privileges: true"," prevents any child process from gaining privileges via setuid\u002Fsetgid binaries",[230,2863,2864,1888,2867,2870,2871,2873],{},[158,2865,2866],{},"Non-root execution:",[335,2868,2869],{},"entrypoint.sh"," creates a user with the configured PUID\u002FPGID and uses ",[335,2872,2809],{}," to drop from root to that user before starting the application",[154,2875,2876,2879],{},[158,2877,2878],{},"Optional additional hardening"," for advanced users:",[2881,2882,2886],"pre",{"className":2883,"code":2884,"language":2368,"meta":2885,"style":2885},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Add to your docker-compose.yml for maximum lockdown:\nservices:\n  capacitarr:\n    read_only: true        # Immutable root filesystem\n    tmpfs:\n      - \u002Ftmp:size=10M      # Writable temp directory in RAM\n    user: \"1001:1001\"      # Fixed UID\u002FGID (replaces PUID\u002FPGID env vars)\n","",[335,2887,2888,2896,2906,2913,2928,2935,2947],{"__ignoreMap":2885},[2889,2890,2892],"span",{"class":2891,"line":17},"line",[2889,2893,2895],{"class":2894},"sHwdD","# Add to your docker-compose.yml for maximum lockdown:\n",[2889,2897,2898,2902],{"class":2891,"line":22},[2889,2899,2901],{"class":2900},"swJcz","services",[2889,2903,2905],{"class":2904},"sMK4o",":\n",[2889,2907,2908,2911],{"class":2891,"line":27},[2889,2909,2910],{"class":2900},"  capacitarr",[2889,2912,2905],{"class":2904},[2889,2914,2915,2918,2921,2925],{"class":2891,"line":53},[2889,2916,2917],{"class":2900},"    read_only",[2889,2919,2920],{"class":2904},":",[2889,2922,2924],{"class":2923},"sfNiH"," true",[2889,2926,2927],{"class":2894},"        # Immutable root filesystem\n",[2889,2929,2930,2933],{"class":2891,"line":116},[2889,2931,2932],{"class":2900},"    tmpfs",[2889,2934,2905],{"class":2904},[2889,2936,2937,2940,2944],{"class":2891,"line":66},[2889,2938,2939],{"class":2904},"      -",[2889,2941,2943],{"class":2942},"sfazB"," \u002Ftmp:size=10M",[2889,2945,2946],{"class":2894},"      # Writable temp directory in RAM\n",[2889,2948,2950,2953,2955,2958,2961,2964],{"class":2891,"line":2949},7,[2889,2951,2952],{"class":2900},"    user",[2889,2954,2920],{"class":2904},[2889,2956,2957],{"class":2904}," \"",[2889,2959,2960],{"class":2942},"1001:1001",[2889,2962,2963],{"class":2904},"\"",[2889,2965,2966],{"class":2894},"      # Fixed UID\u002FGID (replaces PUID\u002FPGID env vars)\n",[2968,2969,2970],"blockquote",{},[154,2971,2972,2796,2975,2978,2979,2982,2983,2986,2987,2990,2991,2993,2994,1850],{},[158,2973,2974],{},"Note:",[335,2976,2977],{},"read_only: true"," requires using ",[335,2980,2981],{},"user:"," instead of ",[335,2984,2985],{},"PUID\u002FPGID"," because the PUID\u002FPGID entrypoint writes to ",[335,2988,2989],{},"\u002Fetc\u002Fpasswd"," at startup. The ",[335,2992,369],{}," volume is always writable regardless of ",[335,2995,2996],{},"read_only",[259,2998,3000],{"id":2999},"supply-chain-security-docker-image-pinning","Supply Chain Security — Docker Image Pinning",[154,3002,3003,3004,3007,3008,3011,3012,3015],{},"All Docker images used in CI pipelines and local ",[335,3005,3006],{},"Makefile"," targets are ",[158,3009,3010],{},"pinned to specific version tags"," rather than ",[335,3013,3014],{},":latest",". This prevents silent supply chain attacks where a compromised upstream image could propagate into our build and security scanning pipeline.",[512,3017,3019],{"id":3018},"pinning-policy","Pinning Policy",[264,3021,3022,3043,3049,3058],{},[230,3023,3024,3030,3031,3033,3034,727,3037,727,3040,374],{},[158,3025,3026,3027,3029],{},"No ",[335,3028,3014],{}," tags:"," Every Docker image reference in CI workflows and ",[335,3032,3006],{}," must use a specific version tag (e.g., ",[335,3035,3036],{},":0.69.3",[335,3038,3039],{},":v2.11.4",[335,3041,3042],{},":3.21",[230,3044,3045,3048],{},[158,3046,3047],{},"No curl-pipe-to-shell:"," CI jobs must not download and execute scripts from external URLs at runtime. All tools must be consumed via their official Docker images",[230,3050,3051,3054,3055,3057],{},[158,3052,3053],{},"Makefile ↔ CI parity:"," Every tool version in CI workflows must match the corresponding version in the ",[335,3056,3006],{},". Both files are updated together. Note: CI uses GitHub Actions (which install tool binaries directly), while the Makefile uses Docker images. Tool versions are kept in sync even though the delivery mechanism differs",[230,3059,3060,3063,3064,3067],{},[158,3061,3062],{},"Digest pinning for runtime image:"," The production Dockerfile runtime base image (",[335,3065,3066],{},"alpine",") is pinned to a specific SHA-256 digest for reproducible, auditable builds",[512,3069,3071],{"id":3070},"regular-re-evaluation","Regular Re-evaluation",[154,3073,3074,3075,3078],{},"Pinned Docker image versions are ",[158,3076,3077],{},"re-evaluated on a regular basis"," to pick up security patches and new features:",[227,3080,3081,3084,3090,3096,3099],{},[230,3082,3083],{},"Check each pinned image for newer stable releases",[230,3085,3086,3087],{},"Pull and test updated versions locally with ",[335,3088,3089],{},"make ci",[230,3091,3092,3093,3095],{},"Update version tags in both ",[335,3094,3006],{}," and CI workflows",[230,3097,3098],{},"Update the Dockerfile runtime base image digest if a new Alpine patch is available",[230,3100,3101,3102],{},"Commit with ",[335,3103,3104],{},"chore(deps): bump \u003Ctool> to v\u003Cversion>",[512,3106,3108],{"id":3107},"currently-pinned-images","Currently Pinned Images",[185,3110,3111,3124],{},[188,3112,3113],{},[191,3114,3115,3118,3121],{},[194,3116,3117],{},"Image",[194,3119,3120],{},"Pinned Version",[194,3122,3123],{},"Purpose",[201,3125,3126,3141,3156,3171,3186,3201,3216,3232,3247,3261,3276,3291],{},[191,3127,3128,3133,3138],{},[206,3129,3130],{},[335,3131,3132],{},"ghcr.io\u002Faquasecurity\u002Ftrivy",[206,3134,3135],{},[335,3136,3137],{},"0.69.3",[206,3139,3140],{},"Filesystem and container image vulnerability scanning",[191,3142,3143,3148,3153],{},[206,3144,3145],{},[335,3146,3147],{},"golangci\u002Fgolangci-lint",[206,3149,3150],{},[335,3151,3152],{},"v2.11.4",[206,3154,3155],{},"Go static analysis and linting",[191,3157,3158,3163,3168],{},[206,3159,3160],{},[335,3161,3162],{},"zricethezav\u002Fgitleaks",[206,3164,3165],{},[335,3166,3167],{},"v8.30.1",[206,3169,3170],{},"Secret scanning in git history",[191,3172,3173,3178,3183],{},[206,3174,3175],{},[335,3176,3177],{},"semgrep\u002Fsemgrep",[206,3179,3180],{},[335,3181,3182],{},"1.155.0",[206,3184,3185],{},"Multi-language SAST scanning",[191,3187,3188,3193,3198],{},[206,3189,3190],{},[335,3191,3192],{},"orhunp\u002Fgit-cliff",[206,3194,3195],{},[335,3196,3197],{},"2.12.0",[206,3199,3200],{},"Changelog generation from commits",[191,3202,3203,3208,3213],{},[206,3204,3205],{},[335,3206,3207],{},"goreleaser\u002Fgoreleaser-action@v6",[206,3209,3210],{},[335,3211,3212],{},"version: v2.14.1",[206,3214,3215],{},"Cross-compiled release binary builds (consumed via GitHub Action, not Docker image)",[191,3217,3218,3224,3229],{},[206,3219,3220,3223],{},[335,3221,3222],{},"google\u002Fgo-containerregistry"," (crane)",[206,3225,3226],{},[335,3227,3228],{},"v0.21.3",[206,3230,3231],{},"Docker image mirroring (GHCR → Docker Hub)",[191,3233,3234,3239,3244],{},[206,3235,3236],{},[335,3237,3238],{},"ghcr.io\u002Fzaproxy\u002Fzaproxy",[206,3240,3241],{},[335,3242,3243],{},"stable",[206,3245,3246],{},"OWASP ZAP DAST scanning (see note below)",[191,3248,3249,3253,3258],{},[206,3250,3251],{},[335,3252,3066],{},[206,3254,3255],{},[335,3256,3257],{},"3.21",[206,3259,3260],{},"Production runtime base image (digest-pinned in Dockerfile)",[191,3262,3263,3268,3273],{},[206,3264,3265],{},[335,3266,3267],{},"node",[206,3269,3270],{},[335,3271,3272],{},"24-alpine",[206,3274,3275],{},"Frontend build and test",[191,3277,3278,3283,3288],{},[206,3279,3280],{},[335,3281,3282],{},"golang",[206,3284,3285],{},[335,3286,3287],{},"1.26.2-alpine",[206,3289,3290],{},"Backend build and test",[191,3292,3293,3299,3304],{},[206,3294,3295,3298],{},[335,3296,3297],{},"pnpm"," (CLI)",[206,3300,3301],{},[335,3302,3303],{},"10.32.1",[206,3305,3306],{},"Node.js package manager (pinned in Makefile and Dockerfile)",[2968,3308,3309],{},[154,3310,3311,3318,3319,3321,3322,3324,3325,3327,3328,3330],{},[158,3312,3313,3314,3317],{},"Note on ZAP ",[335,3315,3316],{},":stable"," tag:"," The OWASP ZAP proxy image (",[335,3320,3238],{},") uses the ",[335,3323,3316],{}," tag because ZAP does not publish individually versioned image tags. The ",[335,3326,3316],{}," tag tracks the latest stable release. This is an accepted exception to the pinning policy — ZAP is used only for local DAST scanning (",[335,3329,1859],{},"), not in CI pipelines, so a compromised image cannot affect builds or releases.",[2968,3332,3333],{},[154,3334,3335,1888,3341,3343,3344,3346,3347,3350],{},[158,3336,3337,3338,2920],{},"Note on ",[335,3339,3340],{},"crane",[335,3342,3340],{}," CLI tool (from ",[335,3345,3222],{},") is used in the release pipeline to mirror multi-arch Docker images from GHCR to Docker Hub. It is currently installed via curl-pipe-to-shell in the GitHub Actions workflow (",[335,3348,3349],{},"release.yml","), which is a known deviation from the \"no curl-pipe-to-shell\" policy. This is being tracked for remediation (e.g., switching to a pre-built Docker image or a pinned GitHub Action).",[154,3352,3353,3356],{},[158,3354,3355],{},"Next reassessment date:"," 2026-04-27",[259,3358,3360],{"id":3359},"important-caveats","Important Caveats",[264,3362,3363,3378,3384],{},[230,3364,3365,3370,3371,3374,3375,3377],{},[158,3366,3367,3369],{},[335,3368,353],{}," trust model:"," When enabled, Capacitarr unconditionally trusts the configured header. The server ",[158,3372,3373],{},"must"," be behind a reverse proxy that sets this header. Direct internet exposure with ",[335,3376,353],{}," enabled allows authentication bypass",[230,3379,3380,3383],{},[158,3381,3382],{},"Single-user design:"," Capacitarr does not implement role-based access control. All authenticated users have full access",[230,3385,3386,3389],{},[158,3387,3388],{},"Local network assumption:"," The security model assumes the application runs on a trusted local network or behind a reverse proxy",[3391,3392,3393],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":2885,"searchDepth":17,"depth":22,"links":3395},[3396,3397,3398,3402],{"id":151,"depth":22,"text":152},{"id":179,"depth":22,"text":180},{"id":222,"depth":22,"text":175,"children":3399},[3400,3401],{"id":261,"depth":27,"text":262},{"id":283,"depth":27,"text":284},{"id":307,"depth":22,"text":308,"children":3403},[3404,3405,3406,3407,3408,3409,3410,3412,3413,3414,3415],{"id":318,"depth":27,"text":319},{"id":357,"depth":27,"text":358},{"id":393,"depth":27,"text":394},{"id":499,"depth":27,"text":500},{"id":1639,"depth":27,"text":1640},{"id":1863,"depth":27,"text":1864},{"id":1945,"depth":27,"text":3411},"Dependency Override Policy (pnpm.overrides)",{"id":2630,"depth":27,"text":2631},{"id":2778,"depth":27,"text":2779},{"id":2999,"depth":27,"text":3000},{"id":3359,"depth":27,"text":3360},"md",null,{},{"order":17},{"title":118,"description":2885},"PxZllbKeZmu-F_vKMBgL-jCSxFEBlD2aizWAT93GaYw",[3423,3425],{"title":113,"path":114,"stem":115,"description":3424,"order":116,"children":-1},"Capacitarr uses a tag-triggered release pipeline powered by git-cliff, GoReleaser, and GitHub Actions. Releases follow Semantic Versioning and are driven by Conventional Commits.",{"title":124,"path":125,"stem":126,"description":3417,"order":53,"children":-1},1776649615433]