[{"data":1,"prerenderedAt":670},["ShallowReactive",2],{"blog-cyber-resilience-act":3},{"id":4,"title":5,"author":6,"authorSlug":7,"body":8,"date":655,"description":656,"extension":657,"faq":7,"featured":658,"image":659,"meta":660,"navigation":661,"path":662,"readTime":7,"seo":663,"stem":664,"tags":665,"__hash__":669},"blog\u002Fblog\u002Fcyber-resilience-act.md","Vue.js and the Cyber Resilience Act: How Developers Can Avoid Mistakes","Epicmax Team",null,{"type":9,"value":10,"toc":641},"minimark",[11,25,30,33,50,53,57,62,89,93,98,101,166,169,212,215,219,222,264,267,315,319,322,513,522,526,541,545,552,570,574,581,585,617,621,624,632,637],[12,13,14,15,19,20,24],"p",{},"The ",[16,17,18],"strong",{},"Cyber Resilience Act (CRA)"," is a new EU regulation that requires all digital products to be ",[21,22,23],"em",{},"secure by default"," and properly maintained throughout their entire lifecycle. The goal is simple: reduce security risks in software and ensure that companies ship products that meet modern cybersecurity standards.",[26,27,29],"h2",{"id":28},"what-is-cra-and-why-does-it-matter-for-frontend-teams","What is CRA and why does it matter for frontend teams?",[12,31,32],{},"For Vue developers and frontend engineering teams, CRA creates concrete technical responsibilities that directly affect product compliance:",[34,35,36,44,47],"ul",{},[37,38,39,40,43],"li",{},"you can't ignore ",[16,41,42],{},"vulnerabilities in npm packages"," or outdated dependencies",[37,45,46],{},"security by design becomes an expected requirement for EU client projects under CRA",[37,48,49],{},"the way we structure code, handle dependencies, and manage updates now directly affects whether clients stay compliant or face legal, financial, or reputational risks",[12,51,52],{},"If you build or maintain Vue applications for European businesses, understanding CRA obligations is essential to avoid security gaps and ensure your software meets EU-level compliance.",[26,54,56],{"id":55},"why-this-matters-for-clients","Why this matters for clients",[12,58,59],{},[16,60,61],{},"Business-friendly arguments:",[63,64,65,71,77,83],"ol",{},[37,66,67,70],{},[16,68,69],{},"Fines and reputation risk."," Under the EU Cyber Resilience Act, companies may face penalties if they ship digital products without proper security measures, maintenance, and vulnerability handling.",[37,72,73,76],{},[16,74,75],{},"Transparency and trust through SBOMs."," Providing an SBOM (software bill of materials) and clear security changelogs demonstrates reliability, professionalism, and proactive risk management. Many enterprise clients now require this as part of vendor evaluation.",[37,78,79,82],{},[16,80,81],{},"Lower long-term maintenance costs."," Continuous small updates, dependency patches, and vulnerability monitoring cost significantly less than urgent large-scale remediation when a critical issue appears.",[37,84,85,88],{},[16,86,87],{},"Competitive advantage in the EU market."," Products that are aligned with CRA requirements, secure by design, or EU-ready gain trust faster.",[26,90,92],{"id":91},"what-vue-developers-should-do-right-now","What Vue developers should do right now",[94,95,97],"h3",{"id":96},"_1-keep-dependencies-healthy","1. Keep dependencies healthy",[12,99,100],{},"Bad dependencies → better alternatives:",[34,102,103,118,131,137,147,157],{},[37,104,105,106,109,110,113,114,117],{},"❌ ",[16,107,108],{},"moment.js"," — deprecated, huge bundle size. ✅ Use ",[16,111,112],{},"date-fns"," or ",[16,115,116],{},"dayjs"," (lightweight, active support).",[37,119,105,120,123,124,113,127,130],{},[16,121,122],{},"request"," — old HTTP client, unmaintained. ✅ Use ",[16,125,126],{},"axios",[16,128,129],{},"ky",".",[37,132,105,133,136],{},[16,134,135],{},"left-pad"," (and similar tiny packages). ✅ Inline small utilities yourself — less supply-chain risk.",[37,138,105,139,142,143,146],{},[16,140,141],{},"lodash (full package)"," — bloats the bundle. ✅ Use ",[16,144,145],{},"lodash-es"," with tree-shaking or minimal libs like just \u002F radash.",[37,148,105,149,152,153,156],{},[16,150,151],{},"crypto-js"," — outdated crypto, known issues. ✅ Use the ",[16,154,155],{},"Web Crypto API"," (built into modern browsers).",[37,158,105,159,162,163,130],{},[16,160,161],{},"core-js@2"," — deprecated. ✅ Upgrade to ",[16,164,165],{},"core-js@3",[12,167,168],{},"Vulnerability checks:",[170,171,176],"pre",{"className":172,"code":173,"language":174,"meta":175,"style":175},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","npx osv-scanner --lockfile pnpm-lock.yaml\nnpx knip --include vue,nuxt,vitest\n","bash","",[177,178,179,198],"code",{"__ignoreMap":175},[180,181,184,188,192,195],"span",{"class":182,"line":183},"line",1,[180,185,187],{"class":186},"sBMFI","npx",[180,189,191],{"class":190},"sfazB"," osv-scanner",[180,193,194],{"class":190}," --lockfile",[180,196,197],{"class":190}," pnpm-lock.yaml\n",[180,199,201,203,206,209],{"class":182,"line":200},2,[180,202,187],{"class":186},[180,204,205],{"class":190}," knip",[180,207,208],{"class":190}," --include",[180,210,211],{"class":190}," vue,nuxt,vitest\n",[12,213,214],{},"Auto-updates: Dependabot or Renovate.",[94,216,218],{"id":217},"_2-block-unsafe-html","2. Block unsafe HTML",[12,220,221],{},"ESLint rule:",[170,223,227],{"className":224,"code":225,"language":226,"meta":175,"style":175},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","rules: { 'vue\u002Fno-v-html': 'error' }\n","js",[177,228,229],{"__ignoreMap":175},[180,230,231,234,238,241,244,247,250,254,256,259,261],{"class":182,"line":183},[180,232,233],{"class":186},"rules",[180,235,237],{"class":236},"sMK4o",":",[180,239,240],{"class":236}," {",[180,242,243],{"class":236}," '",[180,245,246],{"class":190},"vue\u002Fno-v-html",[180,248,249],{"class":236},"'",[180,251,253],{"class":252},"swJcz",": ",[180,255,249],{"class":236},[180,257,258],{"class":190},"error",[180,260,249],{"class":236},[180,262,263],{"class":236}," }\n",[12,265,266],{},"If CMS content requires HTML, wrap it with DOMPurify:",[170,268,272],{"className":269,"code":270,"language":271,"meta":175,"style":175},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Ctemplate>\u003Cdiv v-html=\"$sanitize(html)\">\u003C\u002Fdiv>\u003C\u002Ftemplate>\n","vue",[177,273,274],{"__ignoreMap":175},[180,275,276,279,282,285,288,292,295,298,301,303,306,308,310,312],{"class":182,"line":183},[180,277,278],{"class":236},"\u003C",[180,280,281],{"class":252},"template",[180,283,284],{"class":236},">\u003C",[180,286,287],{"class":252},"div",[180,289,291],{"class":290},"spNyl"," v-html",[180,293,294],{"class":236},"=",[180,296,297],{"class":236},"\"",[180,299,300],{"class":190},"$sanitize(html)",[180,302,297],{"class":236},[180,304,305],{"class":236},">\u003C\u002F",[180,307,287],{"class":252},[180,309,305],{"class":236},[180,311,281],{"class":252},[180,313,314],{"class":236},">\n",[94,316,318],{"id":317},"_3-add-basic-ssr-security-headers","3. Add basic SSR security headers",[12,320,321],{},"For Nuxt projects, you can add these in a few lines:",[170,323,327],{"className":324,"code":325,"language":326,"meta":175,"style":175},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export default defineNuxtConfig({\n  nitro: {\n    routeRules: {\n      '\u002F**': {\n        headers: {\n          'x-content-type-options': 'nosniff',\n          'referrer-policy': 'strict-origin-when-cross-origin',\n          'strict-transport-security': 'max-age=31536000; includeSubDomains',\n          'content-security-policy': \"default-src 'self'\"\n        }\n      }\n    }\n  }\n})\n","ts",[177,328,329,349,359,369,384,394,417,438,459,480,486,492,498,504],{"__ignoreMap":175},[180,330,331,335,338,342,346],{"class":182,"line":183},[180,332,334],{"class":333},"s7zQu","export",[180,336,337],{"class":333}," default",[180,339,341],{"class":340},"s2Zo4"," defineNuxtConfig",[180,343,345],{"class":344},"sTEyZ","(",[180,347,348],{"class":236},"{\n",[180,350,351,354,356],{"class":182,"line":200},[180,352,353],{"class":252},"  nitro",[180,355,237],{"class":236},[180,357,358],{"class":236}," {\n",[180,360,362,365,367],{"class":182,"line":361},3,[180,363,364],{"class":252},"    routeRules",[180,366,237],{"class":236},[180,368,358],{"class":236},[180,370,372,375,378,380,382],{"class":182,"line":371},4,[180,373,374],{"class":236},"      '",[180,376,377],{"class":252},"\u002F**",[180,379,249],{"class":236},[180,381,237],{"class":236},[180,383,358],{"class":236},[180,385,387,390,392],{"class":182,"line":386},5,[180,388,389],{"class":252},"        headers",[180,391,237],{"class":236},[180,393,358],{"class":236},[180,395,397,400,403,405,407,409,412,414],{"class":182,"line":396},6,[180,398,399],{"class":236},"          '",[180,401,402],{"class":252},"x-content-type-options",[180,404,249],{"class":236},[180,406,237],{"class":236},[180,408,243],{"class":236},[180,410,411],{"class":190},"nosniff",[180,413,249],{"class":236},[180,415,416],{"class":236},",\n",[180,418,420,422,425,427,429,431,434,436],{"class":182,"line":419},7,[180,421,399],{"class":236},[180,423,424],{"class":252},"referrer-policy",[180,426,249],{"class":236},[180,428,237],{"class":236},[180,430,243],{"class":236},[180,432,433],{"class":190},"strict-origin-when-cross-origin",[180,435,249],{"class":236},[180,437,416],{"class":236},[180,439,441,443,446,448,450,452,455,457],{"class":182,"line":440},8,[180,442,399],{"class":236},[180,444,445],{"class":252},"strict-transport-security",[180,447,249],{"class":236},[180,449,237],{"class":236},[180,451,243],{"class":236},[180,453,454],{"class":190},"max-age=31536000; includeSubDomains",[180,456,249],{"class":236},[180,458,416],{"class":236},[180,460,462,464,467,469,471,474,477],{"class":182,"line":461},9,[180,463,399],{"class":236},[180,465,466],{"class":252},"content-security-policy",[180,468,249],{"class":236},[180,470,237],{"class":236},[180,472,473],{"class":236}," \"",[180,475,476],{"class":190},"default-src 'self'",[180,478,479],{"class":236},"\"\n",[180,481,483],{"class":182,"line":482},10,[180,484,485],{"class":236},"        }\n",[180,487,489],{"class":182,"line":488},11,[180,490,491],{"class":236},"      }\n",[180,493,495],{"class":182,"line":494},12,[180,496,497],{"class":236},"    }\n",[180,499,501],{"class":182,"line":500},13,[180,502,503],{"class":236},"  }\n",[180,505,507,510],{"class":182,"line":506},14,[180,508,509],{"class":236},"}",[180,511,512],{"class":344},")\n",[12,514,515,516,521],{},"If your team needs help implementing a secure Nuxt setup from scratch, our ",[517,518,520],"a",{"href":519},"\u002Fservices\u002Fnuxt-development","Nuxt.js development services"," include architecture and security configuration.",[94,523,525],{"id":524},"_4-secrets-server-only","4. Secrets → server only",[12,527,528,529,532,533,536,537,540],{},"Private keys stay in ",[177,530,531],{},".env",", only variables prefixed with ",[177,534,535],{},"PUBLIC_"," go to the client. Always add ",[177,538,539],{},".env.example"," so colleagues know what's needed. Never store tokens in localStorage → use HttpOnly cookies.",[94,542,544],{"id":543},"_5-transparency-documentation","5. Transparency & documentation",[12,546,547,548,551],{},"Add a ",[16,549,550],{},"SECURITY.md"," with instructions on how to report vulnerabilities and fix timelines (Critical ≤ 72h, High ≤ 7d). Add a dedicated Security section to your changelog. Generate an SBOM at release time:",[170,553,555],{"className":172,"code":554,"language":174,"meta":175,"style":175},"npx @cyclonedx\u002Fcyclonedx-npm --output sbom.json\n",[177,556,557],{"__ignoreMap":175},[180,558,559,561,564,567],{"class":182,"line":183},[180,560,187],{"class":186},[180,562,563],{"class":190}," @cyclonedx\u002Fcyclonedx-npm",[180,565,566],{"class":190}," --output",[180,568,569],{"class":190}," sbom.json\n",[94,571,573],{"id":572},"_6-lightweight-incident-process","6. Lightweight incident process",[12,575,576,577,580],{},"Create a ",[177,578,579],{},"#security-incidents"," channel (Slack\u002FTeams). Use a simple ticket template: \"Issue → Affected → Temporary mitigation → Fix timeline.\" This builds trust and discipline without bureaucracy.",[26,582,584],{"id":583},"how-to-introduce-this-without-pain","How to introduce this without pain",[34,586,587,600,603],{},[37,588,589,590,593,594,597,598,130],{},"Start with ",[16,591,592],{},"one PR",": add ",[177,595,596],{},"dependency-review.yml"," + ",[177,599,550],{},[37,601,602],{},"Then gradually add: CSP → SafeHtml → Renovate.",[37,604,605,606],{},"Don't ask the client to \"rewrite everything.\" Instead, show small wins:\n",[34,607,608,611,614],{},[37,609,610],{},"Replacing moment.js → bundle size drops by 200 KB.",[37,612,613],{},"Adding CSP → browser blocks XSS attempts.",[37,615,616],{},"Generating SBOM → client can show auditors what's inside.",[26,618,620],{"id":619},"bottom-line","Bottom line",[12,622,623],{},"CRA isn't just red tape — it's an opportunity to strengthen security and maintenance practices before they become a problem. For Vue developers working in outstaff and service companies, being future-proof means keeping dependencies up to date, building applications with security by design, and documenting security processes so clients can clearly demonstrate compliance.",[12,625,626,627,631],{},"At Epicmax, we help companies turn these requirements into clear, actionable steps. We can ",[517,628,630],{"href":629},"\u002Fservices\u002Fcode-audit","audit your existing Vue or Nuxt application",", identify security and compliance gaps, and provide a practical, prioritized action plan — so you know exactly what to fix, why it matters, and how to do it without unnecessary refactoring.",[633,634],"blog-button",{"label":635,"to":636},"Get a CRA-ready audit","https:\u002F\u002Fcalendar.app.google\u002FXwRzASZitZRSVLxq6",[638,639,640],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}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);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}",{"title":175,"searchDepth":361,"depth":361,"links":642},[643,644,645,653,654],{"id":28,"depth":200,"text":29},{"id":55,"depth":200,"text":56},{"id":91,"depth":200,"text":92,"children":646},[647,648,649,650,651,652],{"id":96,"depth":361,"text":97},{"id":217,"depth":361,"text":218},{"id":317,"depth":361,"text":318},{"id":524,"depth":361,"text":525},{"id":543,"depth":361,"text":544},{"id":572,"depth":361,"text":573},{"id":583,"depth":200,"text":584},{"id":619,"depth":200,"text":620},"2025-01-15","The EU Cyber Resilience Act makes software secure-by-default a legal requirement. Here's what Vue and Nuxt developers should do right now to stay compliant.","md",false,"\u002Fimages\u002Fblog\u002Fcyber-resilience-act\u002Fcard.png",{},true,"\u002Fblog\u002Fcyber-resilience-act",{"title":5,"description":656},"blog\u002Fcyber-resilience-act",[666,667,668],"Vue.js","Security","Nuxt","be2HbsdNr1-i1OaLLeNuXszXFpeg7InN0D8kHekce5c",1784559991784]