[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"sanity-M2r8cwH1kOxVeaMPiKe6ykxsUJGFA_bYvAIwBmkv3m0":3,"sanity-KfeVc3X55scTylhgJFEq-r9I3vnddZ7BCNC-Zz93VTQ":733},{"data":4,"sourceMap":-1},{"latestPodcast":5,"latestReleases":14,"post":39,"recent":708},[6],{"_id":7,"publishedAt":8,"slug":9,"sponsored":12,"title":13},"d81860b7-7b72-4ba5-8ad5-3b77fd9a8e9b","2026-07-14T07:40:00.000Z",{"_type":10,"current":11},"slug","your-ai-is-only-as-responsible-as-you-are",null,"Your AI is only as responsible as you are",[15,21,27,33],{"_id":16,"publishedAt":17,"slug":18,"title":20},"eb5b66eb-9410-4329-83bb-22bbff39402a","2026-04-28T13:00:00.000Z",{"_type":10,"current":19},"turn-scattered-knowledge-into-trusted-intelligence","Turning scattered knowledge into trusted intelligence: Stack Internal 2026.3",{"_id":22,"publishedAt":23,"slug":24,"title":26},"369c2401-b62e-4a37-8ff8-bf603023ecad","2026-03-02T15:03:00.988Z",{"_type":10,"current":25},"what-s-new-at-stack-overflow-march-2026","What’s new at Stack Overflow: March 2026",{"_id":28,"publishedAt":29,"slug":30,"title":32},"5e9053a4-07ea-447c-91ea-29e0b6228537","2026-02-02T15:00:00.000Z",{"_type":10,"current":31},"what-s-new-at-stack-overflow-february-2026","What’s new at Stack Overflow: February 2026",{"_id":34,"publishedAt":35,"slug":36,"title":38},"a1b538eb-a8a6-46d0-80a1-ac70ec9bb935","2026-01-05T10:00:00.000-05:00",{"_type":10,"current":37},"what-s-new-at-stack-overflow-january-2026","What’s new at Stack Overflow: January 2026",{"_createdAt":40,"_id":41,"_rev":42,"_type":43,"_updatedAt":44,"author":45,"body":61,"comments":662,"dateUrl":663,"excerpt":519,"image":664,"legacyBody":667,"product":12,"publishedAt":670,"slug":671,"sponsored":12,"tags":673,"title":707,"visible":662},"2023-05-25T09:39:12Z","wp-post-13376","dgl3SCUzppW3U2LvCoSX3c","blogPost","2023-07-13T14:55:24Z",[46],{"_createdAt":47,"_id":48,"_rev":49,"_type":50,"_updatedAt":51,"avatar":52,"employee":57,"name":58,"slug":59},"2023-05-23T16:27:18Z","wp-author-128","07ZbrKPSUrjrV4wQ6gBd2a","blogAuthor","2023-08-29T20:06:15Z",{"_type":53,"asset":54},"image",{"_ref":55,"_type":56},"image-264ff12aab2295aaa315eb9d0ac8b509cd760438-1024x1024-jpg","reference","former","Kevin Montrose",{"current":60},"kevinmontrose",[62,118,127,168,178,186,205,213,221,229,237,245,349,368,386,404,422,440,458,466,474,515,520,537,545,553,568,608,616,624],{"_key":63,"_type":64,"children":65,"markDefs":107,"style":117},"650b0e49cf7c","block",[66,71,76,80,85,89,94,98,103],{"_key":67,"_type":68,"marks":69,"text":70},"650b0e49cf7c0","span",[],"As of September 23rd, 2019, we’re applying ",{"_key":72,"_type":68,"marks":73,"text":75},"650b0e49cf7c1",[74],"d2a6277fe7b0","static analysis",{"_key":77,"_type":68,"marks":78,"text":79},"650b0e49cf7c2",[]," to some of the code behind public ",{"_key":81,"_type":68,"marks":82,"text":84},"650b0e49cf7c3",[83],"d9ccf56b0659","Stack Overflow",{"_key":86,"_type":68,"marks":87,"text":88},"650b0e49cf7c4",[],", ",{"_key":90,"_type":68,"marks":91,"text":93},"650b0e49cf7c5",[92],"1e88f610fa34","Stack Overflow for Teams",{"_key":95,"_type":68,"marks":96,"text":97},"650b0e49cf7c6",[],", and ",{"_key":99,"_type":68,"marks":100,"text":102},"650b0e49cf7c7",[101],"aef48be7a5de","Stack Overflow Enterprise",{"_key":104,"_type":68,"marks":105,"text":106},"650b0e49cf7c8",[]," in order to pre-emptively find and eliminate certain kinds of vulnerabilities. How we accomplished this is an interesting story and also illustrative of advancements in .NET’s open source community.\n\nBut first...\n\n",[108,111,113,115],{"_key":74,"_type":109,"href":110,"reference":12},"link","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FStatic_program_analysis",{"_key":83,"_type":109,"href":112,"reference":12},"https:\u002F\u002Fstackoverflow.com\u002F",{"_key":92,"_type":109,"href":114,"reference":12},"https:\u002F\u002Fstackoverflow.com\u002Fteams",{"_key":101,"_type":109,"href":116,"reference":12},"https:\u002F\u002Fstackoverflow.com\u002Fenterprise","normal",{"_key":119,"_type":64,"children":120,"markDefs":125,"style":126},"bc73977f1f4c",[121],{"_key":122,"_type":68,"marks":123,"text":124},"bc73977f1f4c0",[],"What did we have before static analysis?",[],"h2",{"_key":128,"_type":64,"children":129,"markDefs":161,"style":117},"68c2028f317f",[130,134,139,143,148,152,157],{"_key":131,"_type":68,"marks":132,"text":133},"68c2028f317f0",[],"The Stack Overflow codebase has been under continuous development for around a decade, starting all the way back on ",{"_key":135,"_type":68,"marks":136,"text":138},"68c2028f317f1",[137],"157344403c6b","ASP.NET MVC Preview 2",{"_key":140,"_type":68,"marks":141,"text":142},"68c2028f317f2",[],". As .NET has advanced, we’ve adopted tools that encourage safe practice like Razor (which defaults to encoding strings, helping protect against cross site scripting vulnerabilities). We’ve also created new tools that encourage doing things the Right Way™, like ",{"_key":144,"_type":68,"marks":145,"text":147},"68c2028f317f3",[146],"d996d2bd2325","Dapper",{"_key":149,"_type":68,"marks":150,"text":151},"68c2028f317f4",[],", which handles parameterizing SQL automatically while still being an incredibly performant (lite-)",{"_key":153,"_type":68,"marks":154,"text":156},"68c2028f317f5",[155],"796e7e6be4e1","ORM",{"_key":158,"_type":68,"marks":159,"text":160},"68c2028f317f6",[],".\n\nAn incomplete, but illustrative, list of default-safe patterns in our codebase:\n\n",[162,164,166],{"_key":137,"_type":109,"href":163,"reference":12},"https:\u002F\u002Fhaacked.com\u002Farchive\u002F2009\u002F10\u002F01\u002Fasp.net-mvc-preview-2-released.aspx\u002F",{"_key":146,"_type":109,"href":165,"reference":12},"https:\u002F\u002Fgithub.com\u002FStackExchange\u002FDapper",{"_key":155,"_type":109,"href":167,"reference":12},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FObject-relational_mapping",{"_key":169,"_type":64,"children":170,"level":175,"listItem":176,"markDefs":177,"style":117},"663dad0bd512",[171],{"_key":172,"_type":68,"marks":173,"text":174},"663dad0bd5120",[],"Automated SQL parameterization with Dapper",1,"bullet",[],{"_key":179,"_type":64,"children":180,"level":175,"listItem":176,"markDefs":185,"style":117},"a52ededead03",[181],{"_key":182,"_type":68,"marks":183,"text":184},"a52ededead030",[],"Default encoding strings in views with Razor",[],{"_key":187,"_type":64,"children":188,"level":175,"listItem":176,"markDefs":202,"style":117},"b44c9def6d54",[189,193,198],{"_key":190,"_type":68,"marks":191,"text":192},"b44c9def6d540",[],"Requiring ",{"_key":194,"_type":68,"marks":195,"text":197},"b44c9def6d541",[196],"059e5583bc10","cross site request forgery",{"_key":199,"_type":68,"marks":200,"text":201},"b44c9def6d542",[]," (XSRF) tokens for non-idempotent (ie. POST, PUT, DELETE, etc.) routes by default",[203],{"_key":196,"_type":109,"href":204,"reference":12},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCross-site_request_forgery",{"_key":206,"_type":64,"children":207,"level":175,"listItem":176,"markDefs":212,"style":117},"692918d7ae6e",[208],{"_key":209,"_type":68,"marks":210,"text":211},"692918d7ae6e0",[],"HMACs with default expirations and common validation code",[],{"_key":214,"_type":64,"children":215,"level":175,"listItem":176,"markDefs":220,"style":117},"6abcc4fd9df8",[216],{"_key":217,"_type":68,"marks":218,"text":219},"6abcc4fd9df80",[],"Adopting TypeScript—an ongoing process—which increases our confidence around shipping correct JavaScript",[],{"_key":222,"_type":64,"children":223,"level":175,"listItem":176,"markDefs":228,"style":117},"2d2af439e831",[224],{"_key":225,"_type":68,"marks":226,"text":227},"2d2af439e8310",[],"Private data—for Teams and Enterprise— is on separate infrastructure with separate access controls",[],{"_key":230,"_type":64,"children":231,"markDefs":236,"style":117},"3f86b9520e87",[232],{"_key":233,"_type":68,"marks":234,"text":235},"3f86b9520e870",[],"We were safe, at least in theory, from most classes of injection and cross-site-scripting attacks.\n\nSo, ...\n\n",[],{"_key":238,"_type":64,"children":239,"markDefs":244,"style":126},"4ca16d2de8f9",[240],{"_key":241,"_type":68,"marks":242,"text":243},"4ca16d2de8f90",[],"What did static analysis give us?",[],{"_key":246,"_type":64,"children":247,"markDefs":338,"style":117},"b038c90b60a6",[248,252,257,261,266,270,274,279,283,288,292,296,300,304,309,313,317,321,325,329,334],{"_key":249,"_type":68,"marks":250,"text":251},"b038c90b60a60",[],"In large part, ",{"_key":253,"_type":68,"marks":254,"text":256},"b038c90b60a61",[255],"strong","confidence that we were consistently following our pre-established best practices",{"_key":258,"_type":68,"marks":259,"text":260},"b038c90b60a62",[],". Even though our engineers are talented and our tooling is easy to use, we’ve had dozens of people working on Stack Overflow for 10+ years—inevitably, some mistakes slipped into the codebase. Accordingly, most fixes were just moving to doing something “the right way” and pretty minor. Things like “use our route registration attribute instead of ",{"_key":262,"_type":68,"marks":263,"text":265},"b038c90b60a63",[264],"42c527c23534","[HttpPost]",{"_key":267,"_type":68,"marks":268,"text":269},"b038c90b60a64",[],"” or “remove old uses of SHA1 and switch to SHA256.”\n\nThe more “exciting” fixes required introducing new patterns and updating old code to use them. While we had no evidence that any of these were exploited, or even exploitable in practice, we felt it was best to err on the side of caution and address them anyway. We added three new patterns as part of adopting static code analysis:\n\n",{"_key":271,"_type":68,"marks":272,"text":273},"b038c90b60a65",[255],"We replaced uses of ",{"_key":275,"_type":68,"marks":276,"text":278},"b038c90b60a66",[277,255],"c28cd6b339cd","System.Random",{"_key":280,"_type":68,"marks":281,"text":282},"b038c90b60a67",[255]," with an equivalent interface backed by ",{"_key":284,"_type":68,"marks":285,"text":287},"b038c90b60a68",[286,255],"7b35e0f5880c","System.Security.Cryptography.RandomNumberGenerator",{"_key":289,"_type":68,"marks":290,"text":291},"b038c90b60a69",[255],". ",{"_key":293,"_type":68,"marks":294,"text":295},"b038c90b60a610",[],"It is very hard to prove a random number being predictable either is or isn’t safe, so we standardized on always hard to predict.\n\n",{"_key":297,"_type":68,"marks":298,"text":299},"b038c90b60a611",[255],"We now default to forbidding HTTP redirects to domains we do not control, requiring all exceptions be explicitly documented. ",{"_key":301,"_type":68,"marks":302,"text":303},"b038c90b60a612",[],"The concern here is ",{"_key":305,"_type":68,"marks":306,"text":308},"b038c90b60a613",[307],"8c6fb5edfe92","open redirects",{"_key":310,"_type":68,"marks":311,"text":312},"b038c90b60a614",[],", which can be used for phishing or other malicious purposes. Most of our redirects were already appropriately validating this, but the checks were scattered across the codebase. There were a few missing or buggy checks, but we found no evidence of them being exploited.\n\n",{"_key":314,"_type":68,"marks":315,"text":316},"b038c90b60a615",[255],"We strengthened XSRF checks to account for cases where users move between unauthenticated and authenticated states. ",{"_key":318,"_type":68,"marks":319,"text":320},"b038c90b60a616",[],"Our XSRF checks previously assumed there was a single token tied to a user’s identity. Since this changes during authentication, some of our code suppressed this check and relied on other validation (completing an OAuth flow, for example). Even though all cases did have some kind of XSRF prevention, having any opt-outs of our default XSRF checking code is risky. So we decided to improve our checks to handle this case. Our fix was to allow two tokens to be acceptable, briefly, on certain routes.\n\nOur checks run on every PR for Stack Overflow and, additionally (and explicitly), on every Enterprise build—meaning we aren’t just confident that we’re following our best practices today, ",{"_key":322,"_type":68,"marks":323,"text":324},"b038c90b60a617",[255],"we’re confident we will keep following them in the future",{"_key":326,"_type":68,"marks":327,"text":328},"b038c90b60a618",[],".\n\nIn terms of ",{"_key":330,"_type":68,"marks":331,"text":333},"b038c90b60a619",[332],"4bfb4dc2c532","Open Web Application Security Project",{"_key":335,"_type":68,"marks":336,"text":337},"b038c90b60a620",[]," (OWASP) lists, we gained automatic detection of:\n\n",[339,341,343,345,347],{"_key":264,"_type":109,"href":340,"reference":12},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fapi\u002Fsystem.web.mvc.httppostattribute?view=aspnet-mvc-5.2",{"_key":277,"_type":109,"href":342,"reference":12},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fapi\u002Fsystem.random?view=netframework-4.8",{"_key":286,"_type":109,"href":344,"reference":12},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fapi\u002Fsystem.security.cryptography.randomnumbergenerator?view=netframework-4.8",{"_key":307,"_type":109,"href":346,"reference":12},"https:\u002F\u002Fcheatsheetseries.owasp.org\u002Fcheatsheets\u002FUnvalidated_Redirects_and_Forwards_Cheat_Sheet.html",{"_key":332,"_type":109,"href":348,"reference":12},"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FAbout_The_Open_Web_Application_Security_Project",{"_key":350,"_type":64,"children":351,"level":175,"listItem":176,"markDefs":365,"style":117},"7eddf2ee5ca8",[352,356,361],{"_key":353,"_type":68,"marks":354,"text":355},"7eddf2ee5ca80",[],"SQL injection attacks [",{"_key":357,"_type":68,"marks":358,"text":360},"7eddf2ee5ca81",[359],"06a99ec0002d","2017 #1",{"_key":362,"_type":68,"marks":363,"text":364},"7eddf2ee5ca82",[],"]",[366],{"_key":359,"_type":109,"href":367,"reference":12},"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10-2017_A1-Injection",{"_key":369,"_type":64,"children":370,"level":175,"listItem":176,"markDefs":383,"style":117},"dce14d69239d",[371,375,380],{"_key":372,"_type":68,"marks":373,"text":374},"dce14d69239d0",[],"XML external entity (XEE) attacks [",{"_key":376,"_type":68,"marks":377,"text":379},"dce14d69239d1",[378],"c0006da2cc01","2017 #4",{"_key":381,"_type":68,"marks":382,"text":364},"dce14d69239d2",[],[384],{"_key":378,"_type":109,"href":385,"reference":12},"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10-2017_A4-XML_External_Entities_(XXE)",{"_key":387,"_type":64,"children":388,"level":175,"listItem":176,"markDefs":401,"style":117},"051d8208cd31",[389,393,398],{"_key":390,"_type":68,"marks":391,"text":392},"051d8208cd310",[],"Cross site scripting (XSS) attacks [",{"_key":394,"_type":68,"marks":395,"text":397},"051d8208cd311",[396],"474978a1e539","2017 #7",{"_key":399,"_type":68,"marks":400,"text":364},"051d8208cd312",[],[402],{"_key":396,"_type":109,"href":403,"reference":12},"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10-2017_A7-Cross-Site_Scripting_(XSS)",{"_key":405,"_type":64,"children":406,"level":175,"listItem":176,"markDefs":419,"style":117},"e6e1feac67d3",[407,411,416],{"_key":408,"_type":68,"marks":409,"text":410},"e6e1feac67d30",[],"Insecure deserialization [",{"_key":412,"_type":68,"marks":413,"text":415},"e6e1feac67d31",[414],"b48a7d8bfdcc","2017 #8",{"_key":417,"_type":68,"marks":418,"text":364},"e6e1feac67d32",[],[420],{"_key":414,"_type":109,"href":421,"reference":12},"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10-2017_A8-Insecure_Deserialization",{"_key":423,"_type":64,"children":424,"level":175,"listItem":176,"markDefs":437,"style":117},"ee1860c29cfe",[425,429,434],{"_key":426,"_type":68,"marks":427,"text":428},"ee1860c29cfe0",[],"XSRF attacks [",{"_key":430,"_type":68,"marks":431,"text":433},"ee1860c29cfe1",[432],"9f15eb562633","2013 #8",{"_key":435,"_type":68,"marks":436,"text":364},"ee1860c29cfe2",[],[438],{"_key":432,"_type":109,"href":439,"reference":12},"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10_2013-A8-Cross-Site_Request_Forgery_(CSRF)",{"_key":441,"_type":64,"children":442,"level":175,"listItem":176,"markDefs":455,"style":117},"9c09ffb8b9b9",[443,447,452],{"_key":444,"_type":68,"marks":445,"text":446},"9c09ffb8b9b90",[],"Open redirects [",{"_key":448,"_type":68,"marks":449,"text":451},"9c09ffb8b9b91",[450],"2e8327242c1d","2013 #10",{"_key":453,"_type":68,"marks":454,"text":364},"9c09ffb8b9b92",[],[456],{"_key":450,"_type":109,"href":457,"reference":12},"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10_2013-A10-Unvalidated_Redirects_and_Forwards",{"_key":459,"_type":64,"children":460,"markDefs":465,"style":117},"4fda9854f720",[461],{"_key":462,"_type":68,"marks":463,"text":464},"4fda9854f7200",[],"That wraps up what we found and fixed, but…\n\n",[],{"_key":467,"_type":64,"children":468,"markDefs":473,"style":126},"81e13a560161",[469],{"_key":470,"_type":68,"marks":471,"text":472},"81e13a5601610",[],"How did we add static code analysis?",[],{"_key":475,"_type":64,"children":476,"markDefs":508,"style":117},"034e86049d0a",[477,481,486,490,495,499,504],{"_key":478,"_type":68,"marks":479,"text":480},"034e86049d0a0",[],"This is boring because all we did was write ",{"_key":482,"_type":68,"marks":483,"text":485},"034e86049d0a1",[484],"881474585857","a config file",{"_key":487,"_type":68,"marks":488,"text":489},"034e86049d0a2",[]," and add a ",{"_key":491,"_type":68,"marks":492,"text":494},"034e86049d0a3",[493],"57c8525ff092","PackageReference",{"_key":496,"_type":68,"marks":497,"text":498},"034e86049d0a4",[]," to ",{"_key":500,"_type":68,"marks":501,"text":503},"034e86049d0a5",[502],"15f3d7ab8e7c","SecurityCodeScan",{"_key":505,"_type":68,"marks":506,"text":507},"034e86049d0a6",[],".\n\nThat’s it. Visual Studio will pick it up as an analyzer (so you get squigglies) and the C# compiler will do the same so you get warnings or errors as desired.\n\n",[509,511,513],{"_key":484,"_type":109,"href":510,"reference":12},"https:\u002F\u002Fgist.github.com\u002Fkevin-montrose\u002F8d5e50fab94fe0e77f3597468c627f61",{"_key":493,"_type":109,"href":512,"reference":12},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fnuget\u002Fconsume-packages\u002Fpackage-references-in-project-files",{"_key":502,"_type":109,"href":514,"reference":12},"https:\u002F\u002Fsecurity-code-scan.github.io\u002F",{"_key":516,"_type":53,"alt":12,"asset":517,"caption":519,"markDefs":12},"7b4abff2ebe9",{"_ref":518,"_type":56},"image-0f98b2b6f86f830b280247bcfecae28f18894d7c-915x212-png","",{"_key":521,"_type":64,"children":522,"markDefs":536,"style":117},"1240ec4b4f81",[523,527,532],{"_key":524,"_type":68,"marks":525,"text":526},"1240ec4b4f810",[],"Far more interesting is all the open source ",{"_key":528,"_type":68,"marks":529,"text":531},"1240ec4b4f811",[530],"em","stuff",{"_key":533,"_type":68,"marks":534,"text":535},"1240ec4b4f812",[]," that made this possible:\n\n",[],{"_key":538,"_type":64,"children":539,"level":175,"listItem":176,"markDefs":544,"style":117},"b45a9d032e7d",[540],{"_key":541,"_type":68,"marks":542,"text":543},"b45a9d032e7d0",[],"In 2014, Microsoft open sourced Roslyn, their C# and VB.NET compiler.",[],{"_key":546,"_type":64,"children":547,"level":175,"listItem":176,"markDefs":552,"style":117},"143cf10c3d22",[548],{"_key":549,"_type":68,"marks":550,"text":551},"143cf10c3d220",[],"Visual Studio 2015 ships with support of Roslyn analyzers.",[],{"_key":554,"_type":64,"children":555,"level":175,"listItem":176,"markDefs":565,"style":117},"b50626e3b7e3",[556,561],{"_key":557,"_type":68,"marks":558,"text":560},"b50626e3b7e30",[559],"c600cd55e30a","The authors",{"_key":562,"_type":68,"marks":563,"text":564},"b50626e3b7e31",[]," of Security Code Scan start work in 2016.",[566],{"_key":559,"_type":109,"href":567,"reference":12},"https:\u002F\u002Fgithub.com\u002Fsecurity-code-scan\u002Fsecurity-code-scan\u002Fgraphs\u002Fcontributors",{"_key":569,"_type":64,"children":570,"level":175,"listItem":176,"markDefs":601,"style":117},"bac36f6fe3a9",[571,575,580,584,589,592,597],{"_key":572,"_type":68,"marks":573,"text":574},"bac36f6fe3a90",[],"I contribute ",{"_key":576,"_type":68,"marks":577,"text":579},"bac36f6fe3a91",[578],"ac1d60213dda","some",{"_key":581,"_type":68,"marks":582,"text":583},"bac36f6fe3a92",[]," ",{"_key":585,"_type":68,"marks":586,"text":588},"bac36f6fe3a93",[587],"25197e4a3a44","minor",{"_key":590,"_type":68,"marks":591,"text":583},"bac36f6fe3a94",[],{"_key":593,"_type":68,"marks":594,"text":596},"bac36f6fe3a95",[595],"d19ce8bc5515","changes",{"_key":598,"_type":68,"marks":599,"text":600},"bac36f6fe3a96",[]," to accommodate Stack Overflow peculiarities in 2019.",[602,604,606],{"_key":578,"_type":109,"href":603,"reference":12},"https:\u002F\u002Fgithub.com\u002Fsecurity-code-scan\u002Fsecurity-code-scan\u002Fpull\u002F137",{"_key":587,"_type":109,"href":605,"reference":12},"https:\u002F\u002Fgithub.com\u002Fsecurity-code-scan\u002Fsecurity-code-scan\u002Fpull\u002F133",{"_key":595,"_type":109,"href":607,"reference":12},"https:\u002F\u002Fgithub.com\u002Fsecurity-code-scan\u002Fsecurity-code-scan\u002Fpull\u002F127",{"_key":609,"_type":64,"children":610,"markDefs":615,"style":117},"f1e01a8b6055",[611],{"_key":612,"_type":68,"marks":613,"text":614},"f1e01a8b60550",[],"If you’d told me six years ago that we’d be able to add any sort of code analysis to the Stack Overflow solution trivially, for free, and in a way that contributes back to the greater developer community, I wouldn’t have believed you. It’s great to see “the new Microsoft” behavior benefit us so directly, but it’s even greater to see what the OSS community has built because of it.\n\nWe’ve only just shipped this, which begs the question...\n\n",[],{"_key":617,"_type":64,"children":618,"markDefs":623,"style":126},"db54a26f1638",[619],{"_key":620,"_type":68,"marks":621,"text":622},"db54a26f16380",[],"What’s next with static code analysis?",[],{"_key":625,"_type":64,"children":626,"markDefs":659,"style":117},"0b18d55ae960",[627,631,635,638,642,646,650,655],{"_key":628,"_type":68,"marks":629,"text":630},"0b18d55ae9600",[255],"Security is an ongoing process, not a bit you flip or a feature you add",{"_key":632,"_type":68,"marks":633,"text":634},"0b18d55ae9601",[],". Accordingly there will always be more to do and places we want to make improvements, and static code analysis is no different.\n\nAs I alluded to at the start, we’re only analyzing ",{"_key":636,"_type":68,"marks":637,"text":579},"0b18d55ae9602",[530],{"_key":639,"_type":68,"marks":640,"text":641},"0b18d55ae9603",[]," of the code behind Stack Overflow. More precisely we’re not analyzing views or tracing through interprocedural calls—analyzing both is an obvious next step.\n\nWe’ll be able to start analyzing views once our migration to ASP.NET Core is complete. Pre-Core Razor view compilation doesn’t give us an easy way to add any analyzers, but that ",{"_key":643,"_type":68,"marks":644,"text":645},"0b18d55ae9604",[530],"should",{"_key":647,"_type":68,"marks":648,"text":649},"0b18d55ae9605",[]," be trivial once we’re upgraded. Razor's default behavior gives us some confidence around injection attacks, and views usually aren’t doing anything scary—but it will be nice to have stronger guarantees of correctness in the future.\n\nNot tracing through interprocedural calls is a bit more complicated. Technically, this is a limitation of Security Code Scan, as ",{"_key":651,"_type":68,"marks":652,"text":654},"0b18d55ae9606",[653],"7e2dc47d9be1","there’s an issue for it",{"_key":656,"_type":68,"marks":657,"text":658},"0b18d55ae9607",[],". That we can’t analyze views reduces the value of interprocedural analysis today, as we almost always pass user-provided data into views. For now, we’re comfortable focusing on our controller action methods since basically all user-provided data passes through them before going onto views or other interprocedural calls.\n\nThe beauty of open source is that when we do come back and do these next steps (and any other quality of life changes), we’ll be making them available to the community so everyone benefits. It’s a wonderful thing to be able to benefit ourselves, our customers, and .NET developers everywhere—all at the same time.\n",[660],{"_key":653,"_type":109,"href":661,"reference":12},"https:\u002F\u002Fgithub.com\u002Fsecurity-code-scan\u002Fsecurity-code-scan\u002Fissues\u002F110",true,"2019\u002F10\u002F08",{"_type":53,"asset":665},{"_ref":666,"_type":56},"image-1b1d4e8a86aaf02e354b752cae58be5c6b253076-5184x3456-jpg",{"code":668,"language":669},"\u003C!-- wp:paragraph -->\nAs of September 23rd, 2019, we’re applying \u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FStatic_program_analysis\">static analysis\u003C\u002Fa> to some of the code behind public \u003Ca href=\"https:\u002F\u002Fstackoverflow.com\u002F\">Stack Overflow\u003C\u002Fa>, \u003Ca href=\"https:\u002F\u002Fstackoverflow.com\u002Fteams\">Stack Overflow for Teams\u003C\u002Fa>, and \u003Ca href=\"https:\u002F\u002Fstackoverflow.com\u002Fenterprise\">Stack Overflow Enterprise\u003C\u002Fa> in order to pre-emptively find and eliminate certain kinds of vulnerabilities. How we accomplished this is an interesting story and also illustrative of advancements in .NET’s open source community.\u003Cbr>\u003Cbr>But first...\u003Cbr>\u003Cbr>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading -->\n\n\u003Ch2>What did we have before static analysis?\u003C\u002Fh2>\n\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\nThe Stack Overflow codebase has been under continuous development for around a decade, starting all the way back on \u003Ca href=\"https:\u002F\u002Fhaacked.com\u002Farchive\u002F2009\u002F10\u002F01\u002Fasp.net-mvc-preview-2-released.aspx\u002F\">ASP.NET MVC Preview 2\u003C\u002Fa>. As .NET has advanced, we’ve adopted tools that encourage safe practice like Razor (which defaults to encoding strings, helping protect against cross site scripting vulnerabilities). We’ve also created new tools that encourage doing things the Right Way™, like \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FStackExchange\u002FDapper\">Dapper\u003C\u002Fa>, which handles parameterizing SQL automatically while still being an incredibly performant (lite-)\u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FObject-relational_mapping\">ORM\u003C\u002Fa>.\u003Cbr>\u003Cbr>An incomplete, but illustrative, list of default-safe patterns in our codebase:\u003Cbr>\u003Cbr>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:list -->\n\n\u003Cul>\u003Cli>Automated SQL parameterization with Dapper\u003C\u002Fli>\u003Cli>Default encoding strings in views with Razor\u003C\u002Fli>\u003Cli>Requiring \u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCross-site_request_forgery\">cross site request forgery\u003C\u002Fa> (XSRF) tokens for non-idempotent (ie. POST, PUT, DELETE, etc.) routes by default\u003C\u002Fli>\u003Cli>HMACs with default expirations and common validation code\u003C\u002Fli>\u003Cli>Adopting TypeScript—an ongoing process—which increases our confidence around shipping correct JavaScript\u003C\u002Fli>\u003Cli>Private data—for Teams and Enterprise— is on separate infrastructure with separate access controls\u003C\u002Fli>\u003C\u002Ful>\n\n\u003C!-- \u002Fwp:list -->\n\n\u003C!-- wp:paragraph -->\nWe were safe, at least in theory, from most classes of injection and cross-site-scripting attacks.\u003Cbr>\u003Cbr>So, ...\u003Cbr>\u003Cbr>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading -->\n\n\u003Ch2>What did static analysis give us?\u003C\u002Fh2>\n\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\nIn large part, \u003Cstrong>confidence that we were consistently following our pre-established best practices\u003C\u002Fstrong>. Even though our engineers are talented and our tooling is easy to use, we’ve had dozens of people working on Stack Overflow for 10+ years—inevitably, some mistakes slipped into the codebase. Accordingly, most fixes were just moving to doing something “the right way” and pretty minor. Things like “use our route registration attribute instead of \u003Ca href=\"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fapi\u002Fsystem.web.mvc.httppostattribute?view=aspnet-mvc-5.2\">[HttpPost]\u003C\u002Fa>” or “remove old uses of SHA1 and switch to SHA256.”\u003Cbr>\u003Cbr>The more “exciting” fixes required introducing new patterns and updating old code to use them. While we had no evidence that any of these were exploited, or even exploitable in practice, we felt it was best to err on the side of caution and address them anyway. We added three new patterns as part of adopting static code analysis:\u003Cbr>\u003Cbr>\u003Cstrong>We replaced uses of \u003C\u002Fstrong>\u003Ca href=\"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fapi\u002Fsystem.random?view=netframework-4.8\">\u003Cstrong>System.Random\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> with an equivalent interface backed by \u003C\u002Fstrong>\u003Ca href=\"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fapi\u002Fsystem.security.cryptography.randomnumbergenerator?view=netframework-4.8\">\u003Cstrong>System.Security.Cryptography.RandomNumberGenerator\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong>. \u003C\u002Fstrong>It is very hard to prove a random number being predictable either is or isn’t safe, so we standardized on always hard to predict.\u003Cbr>\u003Cbr>\u003Cstrong>We now default to forbidding HTTP redirects to domains we do not control, requiring all exceptions be explicitly documented. \u003C\u002Fstrong>The concern here is \u003Ca href=\"https:\u002F\u002Fcheatsheetseries.owasp.org\u002Fcheatsheets\u002FUnvalidated_Redirects_and_Forwards_Cheat_Sheet.html\">open redirects\u003C\u002Fa>, which can be used for phishing or other malicious purposes. Most of our redirects were already appropriately validating this, but the checks were scattered across the codebase. There were a few missing or buggy checks, but we found no evidence of them being exploited.\u003Cbr>\u003Cbr>\u003Cstrong>We strengthened XSRF checks to account for cases where users move between unauthenticated and authenticated states. \u003C\u002Fstrong>Our XSRF checks previously assumed there was a single token tied to a user’s identity. Since this changes during authentication, some of our code suppressed this check and relied on other validation (completing an OAuth flow, for example). Even though all cases did have some kind of XSRF prevention, having any opt-outs of our default XSRF checking code is risky. So we decided to improve our checks to handle this case. Our fix was to allow two tokens to be acceptable, briefly, on certain routes.&nbsp;\u003Cbr>\u003Cbr>Our checks run on every PR for Stack Overflow and, additionally (and explicitly), on every Enterprise build—meaning we aren’t just confident that we’re following our best practices today, \u003Cstrong>we’re confident we will keep following them in the future\u003C\u002Fstrong>.\u003Cbr>\u003Cbr>In terms of \u003Ca href=\"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FAbout_The_Open_Web_Application_Security_Project\">Open Web Application Security Project\u003C\u002Fa> (OWASP) lists, we gained automatic detection of:\u003Cbr>\u003Cbr>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:list -->\n\n\u003Cul>\u003Cli>SQL injection attacks [\u003Ca href=\"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10-2017_A1-Injection\">2017 #1\u003C\u002Fa>]\u003C\u002Fli>\u003Cli>XML external entity (XEE) attacks [\u003Ca href=\"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10-2017_A4-XML_External_Entities_(XXE)\">2017 #4\u003C\u002Fa>]\u003C\u002Fli>\u003Cli>Cross site scripting (XSS) attacks [\u003Ca href=\"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10-2017_A7-Cross-Site_Scripting_(XSS)\">2017 #7\u003C\u002Fa>]\u003C\u002Fli>\u003Cli>Insecure deserialization [\u003Ca href=\"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10-2017_A8-Insecure_Deserialization\">2017 #8\u003C\u002Fa>]\u003C\u002Fli>\u003Cli>XSRF attacks [\u003Ca href=\"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10_2013-A8-Cross-Site_Request_Forgery_(CSRF)\">2013 #8\u003C\u002Fa>]\u003C\u002Fli>\u003Cli>Open redirects [\u003Ca href=\"https:\u002F\u002Fwww.owasp.org\u002Findex.php\u002FTop_10_2013-A10-Unvalidated_Redirects_and_Forwards\">2013 #10\u003C\u002Fa>]\u003C\u002Fli>\u003C\u002Ful>\n\n\u003C!-- \u002Fwp:list -->\n\n\u003C!-- wp:paragraph -->\nThat wraps up what we found and fixed, but…\u003Cbr>\u003Cbr>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading -->\n\n\u003Ch2>How did we add static code analysis?\u003C\u002Fh2>\n\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\nThis is boring because all we did was write \u003Ca href=\"https:\u002F\u002Fgist.github.com\u002Fkevin-montrose\u002F8d5e50fab94fe0e77f3597468c627f61\">a config file\u003C\u002Fa> and add a \u003Ca href=\"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fnuget\u002Fconsume-packages\u002Fpackage-references-in-project-files\">PackageReference\u003C\u002Fa> to \u003Ca href=\"https:\u002F\u002Fsecurity-code-scan.github.io\u002F\">SecurityCodeScan\u003C\u002Fa>.\u003Cbr>\u003Cbr>That’s it. Visual Studio will pick it up as an analyzer (so you get squigglies) and the C# compiler will do the same so you get warnings or errors as desired.\u003Cbr>\u003Cbr>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:image -->\n\u003Cfigure class=\"wp-block-image\">\u003Cimg src=\"https:\u002F\u002Flh5.googleusercontent.com\u002Ftp_UNEwINUu8S4BU2fMU2iOo1tGIOkov4DWJJ86TKGLTqdemBzQ-_8lT7e_pXG9NESzpR8G8MskE4P69B8ZHqjASwYAZ9ASLb1AXbdzvKr_Rcr_mdBiyyxHL1lrGrymEw440Ov1v\" alt=\"\"\u002F>\n\n\u003Cfigcaption> Not real code. By the time I thought to take a screenshot, we’d already fixed everything. \u003C\u002Ffigcaption>\n\n\u003C\u002Ffigure>\n\u003C!-- \u002Fwp:image -->\n\n\u003C!-- wp:paragraph -->\nFar more interesting is all the open source \u003Cem>stuff\u003C\u002Fem> that made this possible:\u003Cbr>\u003Cbr>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:list -->\n\n\u003Cul>\u003Cli>In 2014, Microsoft open sourced Roslyn, their C# and VB.NET compiler.\u003C\u002Fli>\u003Cli>Visual Studio 2015 ships with support of Roslyn analyzers.\u003C\u002Fli>\u003Cli>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fsecurity-code-scan\u002Fsecurity-code-scan\u002Fgraphs\u002Fcontributors\">The authors\u003C\u002Fa> of Security Code Scan start work in 2016.\u003C\u002Fli>\u003Cli>I contribute \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fsecurity-code-scan\u002Fsecurity-code-scan\u002Fpull\u002F137\">some\u003C\u002Fa> \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fsecurity-code-scan\u002Fsecurity-code-scan\u002Fpull\u002F133\">minor\u003C\u002Fa> \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fsecurity-code-scan\u002Fsecurity-code-scan\u002Fpull\u002F127\">changes\u003C\u002Fa> to accommodate Stack Overflow peculiarities in 2019.\u003C\u002Fli>\u003C\u002Ful>\n\n\u003C!-- \u002Fwp:list -->\n\n\u003C!-- wp:paragraph -->\nIf you’d told me six years ago that we’d be able to add any sort of code analysis to the Stack Overflow solution trivially, for free, and in a way that contributes back to the greater developer community, I wouldn’t have believed you. It’s great to see “the new Microsoft” behavior benefit us so directly, but it’s even greater to see what the OSS community has built because of it.\u003Cbr>\u003Cbr>We’ve only just shipped this, which begs the question...\u003Cbr>\u003Cbr>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading -->\n\n\u003Ch2>What’s next with static code analysis?\u003C\u002Fh2>\n\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cstrong>Security is an ongoing process, not a bit you flip or a feature you add\u003C\u002Fstrong>. Accordingly there will always be more to do and places we want to make improvements, and static code analysis is no different.\u003Cbr>\u003Cbr>As I alluded to at the start, we’re only analyzing \u003Cem>some\u003C\u002Fem> of the code behind Stack Overflow. More precisely we’re not analyzing views or tracing through interprocedural calls—analyzing both is an obvious next step.\u003Cbr>\u003Cbr>We’ll be able to start analyzing views once our migration to ASP.NET Core is complete. Pre-Core Razor view compilation doesn’t give us an easy way to add any analyzers, but that \u003Cem>should\u003C\u002Fem> be trivial once we’re upgraded. Razor's default behavior gives us some confidence around injection attacks, and views usually aren’t doing anything scary—but it will be nice to have stronger guarantees of correctness in the future.\u003Cbr>\u003Cbr>Not tracing through interprocedural calls is a bit more complicated. Technically, this is a limitation of Security Code Scan, as \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fsecurity-code-scan\u002Fsecurity-code-scan\u002Fissues\u002F110\">there’s an issue for it\u003C\u002Fa>. That we can’t analyze views reduces the value of interprocedural analysis today, as we almost always pass user-provided data into views. For now, we’re comfortable focusing on our controller action methods since basically all user-provided data passes through them before going onto views or other interprocedural calls.\u003Cbr>\u003Cbr>The beauty of open source is that when we do come back and do these next steps (and any other quality of life changes), we’ll be making them available to the community so everyone benefits. It’s a wonderful thing to be able to benefit ourselves, our customers, and .NET developers everywhere—all at the same time.\u003Cbr>\n\u003C!-- \u002Fwp:paragraph -->","html","2019-10-08T15:46:29.000Z",{"current":672},"adding-static-code-analysis-to-stack-overflow",[674,682,684,688,693,698,700,705],{"_createdAt":675,"_id":676,"_rev":677,"_type":678,"_updatedAt":675,"slug":679,"title":681},"2023-05-23T16:43:21Z","wp-tagcat-bulletin","9HpbCsT2tq0xwozQfkc4ih","blogTag",{"current":680},"bulletin","Bulletin",{"_createdAt":675,"_id":676,"_rev":677,"_type":678,"_updatedAt":675,"slug":683,"title":681},{"current":680},{"_createdAt":675,"_id":685,"_rev":677,"_type":678,"_updatedAt":675,"slug":686,"title":687},"wp-tagcat-code-analysis",{"current":687},"code-analysis",{"_createdAt":675,"_id":689,"_rev":677,"_type":678,"_updatedAt":675,"slug":690,"title":692},"wp-tagcat-company",{"current":691},"company","Company",{"_createdAt":675,"_id":694,"_rev":677,"_type":678,"_updatedAt":675,"slug":695,"title":697},"wp-tagcat-engineering",{"current":696},"engineering","Engineering",{"_createdAt":675,"_id":694,"_rev":677,"_type":678,"_updatedAt":675,"slug":699,"title":697},{"current":696},{"_createdAt":675,"_id":701,"_rev":677,"_type":678,"_updatedAt":675,"slug":702,"title":704},"wp-tagcat-stackoverflow",{"current":703},"stackoverflow","Stackoverflow",{"_createdAt":675,"_id":701,"_rev":677,"_type":678,"_updatedAt":675,"slug":706,"title":704},{"current":703},"Adding Static Code Analysis to Stack Overflow",[709,715,721,727],{"_id":710,"publishedAt":711,"slug":712,"sponsored":12,"title":714},"76c9771b-34e6-4d98-8641-ecefc711f0ef","2026-07-06T15:23:34.559Z",{"_type":10,"current":713},"when-the-sensor-starts-thinking-snortml-agentic-ai-and-the-evolving-architecture-of-intrusion-detection","When the sensor starts thinking: SnortML, agentic AI, and the evolving architecture of intrusion detection",{"_id":716,"publishedAt":717,"slug":718,"sponsored":12,"title":720},"28e560af-f0aa-4d46-bd90-f435ad604aa7","2026-06-26T14:00:27.102Z",{"_type":10,"current":719},"paging-charity-how-can-engineering-leaders-avoid-becoming-bond-villains","Paging Charity! How can engineering leaders avoid becoming Bond villains?",{"_id":722,"publishedAt":723,"slug":724,"sponsored":12,"title":726},"4b22c2a3-3779-4966-93eb-5230391dbdce","2026-06-23T14:08:58.595Z",{"_type":10,"current":725},"your-ai-shipped-a-backend-that-boots-that-is-the-whole-problem","Your AI shipped a backend that boots. That is the whole problem.",{"_id":728,"publishedAt":729,"slug":730,"sponsored":12,"title":732},"5cf362e1-fe7b-45af-b69c-914731c6a052","2026-06-23T14:00:00.000Z",{"_type":10,"current":731},"the-2026-developer-survey-is-now-open-for-human-developers-only","The 2026 Developer Survey is now open (for human developers only)!",{"data":734,"sourceMap":-1},{"count":735,"lastTimestamp":736},6,"2023-05-25T09:46:51Z"]