[{"data":1,"prerenderedAt":1082},["ShallowReactive",2],{"$B3SvcJdz-H":3,"pages-links":23,"posts-links":29,"posts-search":58,"blog-dodging-a-critical-cve-with-zta":129,"decks-for-dodging-a-critical-cve-with-zta":810,"meta":1078},{"id":4,"title":5,"author":6,"description":14,"extension":15,"meta":16,"repository":17,"stem":21,"__hash__":22},"meta\u002Fmeta.yml","bmo.dev",{"name":7,"github":8,"gitlab":9,"email":10,"linkedin":11,"mastodon":12,"bio":13},"William Little","https:\u002F\u002Fgithub.com\u002Fbmodotdev\u002F","https:\u002F\u002Fgitlab.com\u002Fbmodotdev\u002F","L29hqTSwqROvoJ8hMTI2","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fwilliam-e-little-jr\u002F","https:\u002F\u002Fdefcon.social\u002F@bmodotdev","I am a Full Stack Software & DevOps Engineer who loves building and experimenting with Software, Linux, & electronics.\n","A personal blog where I share my thoughts, projects, and anything I might find interesting or helpful.\n","yml",{},{"url":18,"git":19,"branch":20},"https:\u002F\u002Fgitlab.com\u002Fbmodotdev\u002Fbmo.dev\u002F","git@gitlab.com:bmodotdev\u002Fbmo.dev.git","main","meta","MPtcc2KHfBCBy4V4SDhZFIvjyZ_Ba4e-yDnx_Ok_KOs",[24],{"path":25,"extension":26,"title":27,"id":28},"\u002Fabout","md","About","pages\u002Fabout.md",[30,34,38,42,46,50,54],{"path":31,"extension":26,"title":32,"id":33},"\u002Fblog\u002Fconventional-commit-caveats","Conventional Commit Caveats","blog\u002Fblog\u002Fconventional-commit-caveats.md",{"path":35,"extension":26,"title":36,"id":37},"\u002Fblog\u002Fdodging-a-critical-cve-with-zta","Dodging a Critical CVE with ZTA","blog\u002Fblog\u002Fdodging-a-critical-cve-with-zta.md",{"path":39,"extension":26,"title":40,"id":41},"\u002Fblog\u002Flog4perl-mixed-screen-appender","Mixed Output Streams with Log4perl Screen Appender","blog\u002Fblog\u002Flog4perl-mixed-screen-appender.md",{"path":43,"extension":26,"title":44,"id":45},"\u002Fblog\u002Fmake-version","make version","blog\u002Fblog\u002Fmake-version.md",{"path":47,"extension":26,"title":48,"id":49},"\u002Fblog\u002Fpadding-oracle-attacks-rubiks-cow","Padding Oracle Attacks & Rubik’s Cow","blog\u002Fblog\u002Fpadding-oracle-attacks-rubiks-cow.md",{"path":51,"extension":26,"title":52,"id":53},"\u002Fblog\u002Ftracing-signals-in-linux","Tracing Signals in Linux","blog\u002Fblog\u002Ftracing-signals-in-linux.md",{"path":55,"extension":26,"title":56,"id":57},"\u002Fblog\u002Fwhy-are-go-modules-in-my-nodes-modules","Why are Go modules in my node_modules?","blog\u002Fblog\u002Fwhy-are-go-modules-in-my-nodes-modules.md",[59,69,81,90,100,108,117],{"path":55,"title":56,"description":60,"tags":61,"thumbnail":64,"created":68},"Let’s dissect this developer pattern that leads to arbitrary code execution across languages.",[62,63],"go","js",{"sm":65,"md":66,"lg":67},"\u002Fassets\u002Fdavid-mitchell-great-british-baking-show_sm.webp","\u002Fassets\u002Fdavid-mitchell-great-british-baking-show_md.webp","\u002Fassets\u002Fdavid-mitchell-great-british-baking-show_lg.webp","2026-04-07",{"path":35,"title":36,"description":70,"tags":71,"thumbnail":76,"created":80},"Let’s discuss tenets of Zero Trust Architecture, Go, gRPC, and why everyone dodged CVE-2026-33186, right?",[72,73,62,74,75],"awk","cve","grpc","zta",{"sm":77,"md":78,"lg":79},"\u002Fassets\u002Fdodging-the-flyer_sm.webp","\u002Fassets\u002Fdodging-the-flyer_md.webp","\u002Fassets\u002Fdodging-the-flyer_lg.webp","2026-04-02",{"path":47,"title":48,"description":82,"tags":83,"thumbnail":85,"created":89},"In this thought experiment, you’ll learn about cryptographic primitives, combinitorical group theory, and along the way, gain a deeper appreciation for 1999’s film, The Matrix. Oh yeah, and a Cow!",[84],"cryptography",{"sm":86,"md":87,"lg":88},"\u002Fassets\u002Fbournemouth_sm.webp","\u002Fassets\u002Fbournemouth_md.webp","\u002Fassets\u002Fbournemouth_lg.webp","2026-03-06",{"path":43,"title":44,"description":91,"tags":92,"thumbnail":95,"created":99},"Here is how I went about implementing a simple versioning system for my Go project using Conventional Commmits, awk, and make.",[72,93,62,94],"git","semver",{"sm":96,"md":97,"lg":98},"\u002Fassets\u002Flego-pieces_sm.webp","\u002Fassets\u002Flego-pieces_md.webp","\u002Fassets\u002Flego-pieces_lg.webp","2025-11-11",{"path":31,"title":32,"description":101,"tags":102,"thumbnail":103,"created":107},"The Conventional Commmits spec aims to bring structure to commit messages so that we write succint yet meaningful commits. That sounds great, but let’s examine some potential drawbacks.",[93,94],{"sm":104,"md":105,"lg":106},"\u002Fassets\u002Fgit-commit_sm.webp","\u002Fassets\u002Fgit-commit_md.webp","\u002Fassets\u002Fgit-commit_lg.webp","2025-11-07",{"path":39,"title":40,"description":109,"tags":110,"thumbnail":112,"created":116},"Log4perl provides a powerful logging framework, but how can we dynamically and meaninfully use stdout and stderr?",[111],"perl",{"sm":113,"md":114,"lg":115},"\u002Fassets\u002Fperl-latte-onion_100x100.webp","\u002Fassets\u002Fperl-latte-onion_250x250.webp","\u002Fassets\u002Fperl-latte-onion_600x600.webp","2022-02-14",{"path":51,"title":52,"description":118,"tags":119,"thumbnail":124,"created":128},"Signals, or software interrupts, are used to communicate with a process. They could be used to kill a process, restart, or signal a segmentation fault, but how can we know where that signal came from?",[120,121,122,123],"signals","systemtap","strace","linux",{"sm":125,"md":126,"lg":127},"\u002Fassets\u002Ftraffic-light_sm.webp","\u002Fassets\u002Ftraffic-light_md.webp","\u002Fassets\u002Ftraffic-light_lg.webp","2022-02-13",{"id":37,"title":36,"body":130,"created":80,"description":70,"extension":26,"meta":803,"navigation":501,"path":35,"seo":804,"stem":805,"tags":806,"thumbnail":807,"updated":808,"__hash__":809},{"type":131,"value":132,"toc":790},"minimark",[133,140,157,162,189,230,234,247,250,254,257,260,281,287,290,294,308,322,335,339,346,350,366,378,382,385,396,400,410,416,419,422,426,429,432,435,555,558,562,565,762,773,779,783,786],[134,135,136],"p",{},[137,138],"img",{"alt":139,"src":79},"Dodging the Flyer",[134,141,142,143,151,152,156],{},"I’m going to start this by acknowledging that while\n",[144,145,150],"a",{"href":146,"rel":147,"target":149},"https:\u002F\u002Fnvd.nist.gov\u002Fvuln\u002Fdetail\u002FCVE-2026-33186",[148],"nofollow","_blank","CVE-2026-33186","\nwas assigned ",[153,154,155],"em",{},"Critical"," CVSSv3 score of 9.1, I feel this post may\ndramatize the severity.\nMy aim is not to cast blame at the bug, nor the consumers, but to\ndiscuss tenets that hopefully lead most to avoid this vulnerability\nto begin with.",[158,159,161],"h2",{"id":160},"authorization-bypass","Authorization Bypass",[134,163,164,165,170,171,176,177,182,183,188],{},"This CVE concerns ",[144,166,169],{"href":167,"rel":168,"target":149},"https:\u002F\u002Fgithub.com\u002Fgrpc\u002Fgrpc-go",[148],"grpc-go","\nand its ",[144,172,175],{"href":173,"rel":174,"target":149},"https:\u002F\u002Fpkg.go.dev\u002Fgoogle.golang.org\u002Fgrpc\u002Fauthz",[148],"authz","\npackage, a library for enforcing authorization of\n",[144,178,181],{"href":179,"rel":180,"target":149},"https:\u002F\u002Fgrpc.io\u002F",[148],"gRPC"," servers.\ngRPC is primarily used with ",[144,184,187],{"href":185,"rel":186,"target":149},"https:\u002F\u002Fprotobuf.dev",[148],"Protocol Buffers",",\nwhich provide a “language-neutral, platform-neutral, extensible\nmechanism for serializing structured data”.\nIn other words, the two can be used to generate “glue” for remote\nprocedure calls in the language of your choice, leaving the developer\nto focus on business logic.\nThe vulnerability here concerns the interceptor used to authorize gRPC\nrequests before they reach the handler to fulfill them.\nThat is quite a serious vulnerability, but let me explain why I am\nhopeful most were not affected by this.",[134,190,191,192,196,197,200,201,204,205,208,209,212,213,216,217,219,220,223,224,229],{},"So what actually happened?\nBefore grpc-go 1.79.3, the server accepted an HTTP\u002F2 ",[193,194,195],"code",{},":path"," that\nomitted the mandatory leading slash, ",[193,198,199],{},"Service\u002FMethod"," instead of\n",[193,202,203],{},"\u002FService\u002FMethod",", and still routed it to the correct handler.\nPath-based authorization interceptors, the official authz package\n",[153,206,207],{},"and"," any custom interceptor keyed on ",[193,210,211],{},"info.FullMethod",", compared the\nraw, non-canonical string, so a ",[153,214,215],{},"deny"," rule written for\n",[193,218,203],{}," never matched.\nIf your policy then fell back to ",[153,221,222],{},"allow",", the request sailed through;\nsee the\n",[144,225,228],{"href":226,"rel":227,"target":149},"https:\u002F\u002Fgithub.com\u002Fgrpc\u002Fgrpc-go\u002Fsecurity\u002Fadvisories\u002FGHSA-p77j-4mvh-x3m3",[148],"GitHub advisory","\nfor the details.\nHold onto that last condition, we will come back to it.",[158,231,233],{"id":232},"experimental","EXPERIMENTAL",[235,236,237,240],"block-quote",{"link":173},[134,238,239],{},"Notice: This package is EXPERIMENTAL and may be changed or removed in a later release.",[241,242,244],"template",{"v-slot:cite":243},"",[134,245,246],{},"Documentation",[134,248,249],{},"This should have been your first sign.\nIt doesn’t necessarily indicate unstable or insecure, but when a\ndeveloper reports a project as experimental, believe them the first\ntime, especially if that project concerns auth.\nOK, I am being a bit facetious; we shouldn’t blame bugs on consumers.\nIt is important to seek requests-for-comments before going too far\ndown a path, and as such, software may be released without any\nguarantee.\nAs a consumer, you should be vetting your dependencies the best you\ncan.",[158,251,253],{"id":252},"the-middleware-bypass-pattern","The Middleware Bypass Pattern",[134,255,256],{},"Forward-thinking developers wish to write code that is flexible and\neasy to maintain.\nSeparating logic into domains with pluggable interfaces helps us focus\non a single aspect of the design, allows us to rearrange the parts,\ndiscard and add new ones, more easily test and assert their logic.",[134,258,259],{},"The middleware pattern is often a simple function chain, kind of like\nan onion of wrapped closure functions, each layer calling the next.\nA more naive approach is dropping the request directly into a large\nfunction responsible for auth, request tagging, logging, rate-limiting,\ntransport logic, API logic, business logic, etc.\nGrasping the logic of such a function or altering one aspect could\nbring the whole house down.",[134,261,262,263,266,267,270,271,276,277,280],{},"So most developers have pushed auth to a middleware, which ",[153,264,265],{},"should","\nbail before ever reaching a handler to fulfill the request.\nPerhaps a zealous adherence to this ",[153,268,269],{},"Separation of Concerns"," has\nconvinced developers that it’s poor design for handlers to concern\nthemselves with auth.\nThere has been a litany of auth bypass vulnerabilities in these\nmiddlewares, probably most notably,\n",[144,272,275],{"href":273,"rel":274,"target":149},"https:\u002F\u002Fnvd.nist.gov\u002Fvuln\u002Fdetail\u002FCVE-2025-29927",[148],"Next.js CVE-2025-29927","\nvia header “x-middleware-subrequest”, an actual ",[153,278,279],{},"feature"," of the\nframework.",[134,282,283],{},[137,284],{"alt":285,"src":286},"Indiana Jones - No Ticket","\u002Fassets\u002Findiana-jones-no-ticket_lg.webp",[134,288,289],{},"As such, when we use a framework or language that uses a middleware\npattern for auth, we must still require the handlers to check\neveryone’s ticket, even if they’ve somehow managed to make it onto\nthe Zeppelin.",[158,291,293],{"id":292},"zero-trust-architecture","Zero Trust Architecture",[295,296,297,303],"with-caption",{},[134,298,299],{},[137,300],{"alt":301,"src":302},"The X-Files","\u002Fassets\u002Fthe-x-files_lg.webp",[241,304,305],{"v-slot:caption":243},[134,306,307],{},"“Trust No 1” - The X-Files Season 9 Episode 6",[134,309,310,315,316,321],{},[144,311,314],{"href":312,"rel":313,"target":149},"https:\u002F\u002Fcsrc.nist.gov\u002Fpubs\u002Fsp\u002F800\u002F207\u002Ffinal",[148],"NIST SP 800-207","\nand ",[144,317,320],{"href":318,"rel":319,"target":149},"https:\u002F\u002Fcheatsheetseries.owasp.org\u002Fcheatsheets\u002FZero_Trust_Architecture_Cheat_Sheet.html",[148],"OWASP’s ZTA Cheat Sheet","\ngo into great detail about ZTA, but this security principle covers\nsuch a large swath of technology, it can be easy to get lost.\nFrom the perspective of the software developer, especially regarding\nmiddleware auth patterns, ZTA is simply requiring all requests to pass\nthrough a Policy Enforcement Point.",[134,323,324,325,328,329,331,332,334],{},"The ",[153,326,327],{},"footgun"," that developers often pick up, “not all my requests\nrequire auth” or “some are public”; which can further validate the\nfallacy of ",[153,330,269],{}," between auth and handlers.\nThis thought process normalizes auth bypass; it’s a ",[153,333,279],{},".\nBut following ZTA principles for requests that require no auth seems\nlike an oxymoron.\nHow could we possibly do both?",[158,336,338],{"id":337},"trust-but-verify","Trust But Verify",[134,340,341,342,345],{},"Quite simply, you must create a ",[153,343,344],{},"role"," for the un-authenticated, and\nlike any other request, your handler must check the ticket stub\nissued by your auth middleware.\nSo we end up with an authentication token for un-authenticated\nrequests, which sounds silly, but it allows us to enshrine in stone\nthe requirement that all requests must pass a PEP.",[158,347,349],{"id":348},"deny-by-default","Deny by Default",[295,351,352,358],{},[134,353,354],{},[137,355],{"alt":356,"src":357},"ElectroBOOM","\u002Fassets\u002Felectroboom-spark_lg.webp",[241,359,360],{"v-slot:caption":243},[134,361,362],{},[144,363,356],{"href":364,"rel":365,"target":149},"https:\u002F\u002Fwww.electroboom.com",[148],[134,367,368,369,371,372,374,375,377],{},"Perhaps the most egregious fault exposed by this CVE is not following\nthe ZTA tenet to ",[153,370,349],{},".\nIf a route is unrecognized, your middleware should not allow the\nrequest to proceed to the handlers.\nRemember the condition for the bypass: a ",[153,373,215],{}," rule that failed to\nmatch, followed by a fallback ",[153,376,222],{},".\nFlip that default and a malformed route becomes a loud failure rather\nthan an open door.",[158,379,381],{"id":380},"just-in-time","Just In Time",[134,383,384],{},"If we follow ZTA, we’ve essentially established a contract between\nour auth and our downstream consumers.\nBypasses of auth middleware make it obvious why handlers should have\nthe ability to assert a request’s access; however, by accepting this\ndesign pattern, we open the door to Just-In-Time access.\nBy leveraging a simple interface to assert auth, we can now perform\naccess checks potentially at any level of our stack.\nWhere should we begin and where should we end?",[134,386,387,388,391,392,395],{},"You should aim to assert auth at the entry point, our handlers, and\nat the ",[153,389,390],{},"Point of Use"," of that identity; just-in-time before the\nunderlying action takes place.\nWhere you draw the line depends on your project and security needs,\nbut often this means, in the ",[153,393,394],{},"repository",", at the database layer,\nor perhaps just before decryption of records.",[158,397,399],{"id":398},"go-grpc-middleware","Go gRPC Middleware",[134,401,402,403,406,407,409],{},"OK, we’re back to the real vulnerability here. You dodged\n",[144,404,150],{"href":146,"rel":405,"target":149},[148],"\nnot because you “noped out” at the word experimental, a hand-rolled\ninterceptor with a fallback ",[153,408,222],{}," was just as exposed, but because\nevery route your middleware didn’t recognize was denied.\nSo how do we perform auth for gRPC services?",[134,411,412],{},[137,413],{"alt":414,"src":415},"You Wouldn’t Download A Car","\u002Fassets\u002Fyou-wouldnt-download-a-car_lg.webp",[134,417,418],{},"After all, you generated this gRPC service from Protobuf, why\nshouldn’t you use Protobuf to generate a service that has RBAC\ndefinitions baked in?\nWell, if you truly are using Protobuf with multiple languages,\nperhaps diving into the vast and opaque waters of proto3 custom\noptions and gRPC extensions is worth it; however, I believe this\napproach comes with major compromises.",[134,420,421],{},"It becomes quite hard to glean what the RBACs are for any particular\nrequest, and changes to services could appear to make changes to\nRBACs.\nThe last thing we want is an RBAC list in constant flux.\nAs well, this path may lead you to the practice of beginning and\nending your auth at the service or transport level.\nInstead, we want to allow the potential for auth to be asserted\nthroughout our stack.",[158,423,425],{"id":424},"this-is-awk-ward","This is awk-ward",[134,427,428],{},"I propose a rather simple approach, you should hard-code the RBACs\nfor your auto-generated gRPC services, and really, you should never\ntouch them unless broken.",[134,430,431],{},"How do we hard-code values for dynamic services?\nCouldn’t this lead to RBACs that fall out of contract with their\nservices?\nHow do we get the best of both worlds, clear and concrete RBACs, that\nalso reflect an accurate state of the generated gRPC code?",[134,433,434],{},"Well I’m an advocate for awk and stupid-simple solutions, and sometimes\nthey’re manual approaches coupled with automation.\nYou likely leverage some build script, maybe a Makefile, to generate\nyour gRPC and Protobuf, add another target to generate the RBACs:",[436,437,441],"pre",{"className":438,"code":439,"language":440,"meta":243,"style":243},"language-bash shiki shiki-themes houston","awk -F '[[:space:]_]+' '\u002F^\\s*const\\s+\\(\u002F{c=1}\u002F^\\)\u002F{c=0} \\\n  c&&\u002F_FullMethodName\u002F{a[$2][$3] = $6} \\\n  END{for (i in a) { printf \"\\npackage %s\\n\\nconst (\\n\", tolower(i); \\\n    for (j in a[i]) printf \"\\tMethod%-24s = %s\\n\", j, a[i][j]; printf \")\\n\\n\"}}' \\\n    path\u002Fto\u002Fprotos\u002F*.pb.go\n\npackage foo\n\nconst (\n  MethodGetFoo = \"\u002Fapi.v1.Foo\u002FGetFoo\"\n  MethodPutFoo = \"\u002Fapi.v1.Foo\u002FPutFoo\"\n)\n","bash",[193,442,443,461,467,473,483,496,503,512,517,526,538,549],{"__ignoreMap":243},[444,445,448,451,455,458],"span",{"class":446,"line":447},"line",1,[444,449,72],{"class":450},"stHQG",[444,452,454],{"class":453},"se2J0"," -F",[444,456,457],{"class":453}," '[[:space:]_]+'",[444,459,460],{"class":453}," '\u002F^\\s*const\\s+\\(\u002F{c=1}\u002F^\\)\u002F{c=0} \\\n",[444,462,464],{"class":446,"line":463},2,[444,465,466],{"class":453},"  c&&\u002F_FullMethodName\u002F{a[$2][$3] = $6} \\\n",[444,468,470],{"class":446,"line":469},3,[444,471,472],{"class":453},"  END{for (i in a) { printf \"\\npackage %s\\n\\nconst (\\n\", tolower(i); \\\n",[444,474,476,479],{"class":446,"line":475},4,[444,477,478],{"class":453},"    for (j in a[i]) printf \"\\tMethod%-24s = %s\\n\", j, a[i][j]; printf \")\\n\\n\"}}'",[444,480,482],{"class":481},"sLo_3"," \\\n",[444,484,486,489,493],{"class":446,"line":485},5,[444,487,488],{"class":453},"    path\u002Fto\u002Fprotos\u002F",[444,490,492],{"class":491},"s8HWQ","*",[444,494,495],{"class":453},".pb.go\n",[444,497,499],{"class":446,"line":498},6,[444,500,502],{"emptyLinePlaceholder":501},true,"\n",[444,504,506,509],{"class":446,"line":505},7,[444,507,508],{"class":450},"package",[444,510,511],{"class":453}," foo\n",[444,513,515],{"class":446,"line":514},8,[444,516,502],{"emptyLinePlaceholder":501},[444,518,520,523],{"class":446,"line":519},9,[444,521,522],{"class":450},"const",[444,524,525],{"class":481}," (\n",[444,527,529,532,535],{"class":446,"line":528},10,[444,530,531],{"class":450},"  MethodGetFoo",[444,533,534],{"class":453}," =",[444,536,537],{"class":453}," \"\u002Fapi.v1.Foo\u002FGetFoo\"\n",[444,539,541,544,546],{"class":446,"line":540},11,[444,542,543],{"class":450},"  MethodPutFoo",[444,545,534],{"class":453},[444,547,548],{"class":453}," \"\u002Fapi.v1.Foo\u002FPutFoo\"\n",[444,550,552],{"class":446,"line":551},12,[444,553,554],{"class":481},")\n",[134,556,557],{},"That is quite clear and concise, but how do we ensure it’s not fallen\nout of contract with our generated code?\nWell simply, leverage the dynamic function during testing to ensure\nRBACs you have manually enshrined as consts, still exist in the\ngenerated code, and that all routes in your generated code are\nrecognized by your RBACs.\nWhen the test fails, leverage your awk script, inspect the changes,\nand run the tests again.\nYour RBACs are no longer hidden in the generated rat’s nest that can\nbe gRPC output; after all, it is code intended for robots, not humans!\nYou also get a dynamic contract check on build time by leveraging tests.",[158,559,561],{"id":560},"failing-closed","Failing Closed",[134,563,564],{},"Following the awk-ward example above, you should implement a function\nlike below:",[436,566,569],{"className":567,"code":568,"language":62,"meta":243,"style":243},"language-go shiki shiki-themes houston","func RolesForMethod(method string) (roles []user.Role, err error) {\n  switch method {\n  case MethodGetFoo:\n    roles = []user.Role{user.Client, user.UnAuthenticated}\n  case MethodPutFoo:\n    roles = []user.Role{user.Staff, user.Admin}\n  default:\n    err = ErrUnknownMethod\n  }\n  return roles, err\n}\n",[193,570,571,620,632,643,680,689,723,730,740,745,758],{"__ignoreMap":243},[444,572,573,577,580,583,587,590,593,596,599,602,605,608,611,614,617],{"class":446,"line":447},[444,574,576],{"class":575},"sqF6k","func",[444,578,579],{"class":450}," RolesForMethod",[444,581,582],{"class":481},"(",[444,584,586],{"class":585},"smONe","method",[444,588,589],{"class":575}," string",[444,591,592],{"class":481},") (",[444,594,595],{"class":585},"roles",[444,597,598],{"class":481}," []",[444,600,601],{"class":491},"user",[444,603,604],{"class":481},".",[444,606,607],{"class":491},"Role",[444,609,610],{"class":481},", ",[444,612,613],{"class":585},"err",[444,615,616],{"class":575}," error",[444,618,619],{"class":481},") {\n",[444,621,622,625,629],{"class":446,"line":463},[444,623,624],{"class":575},"  switch",[444,626,628],{"class":627},"sb16X"," method",[444,630,631],{"class":481}," {\n",[444,633,634,637,640],{"class":446,"line":469},[444,635,636],{"class":575},"  case",[444,638,639],{"class":627}," MethodGetFoo",[444,641,642],{"class":481},":\n",[444,644,645,648,650,652,654,656,658,661,663,665,668,670,672,674,677],{"class":446,"line":475},[444,646,647],{"class":627},"    roles",[444,649,534],{"class":491},[444,651,598],{"class":481},[444,653,601],{"class":491},[444,655,604],{"class":481},[444,657,607],{"class":491},[444,659,660],{"class":481},"{",[444,662,601],{"class":627},[444,664,604],{"class":481},[444,666,667],{"class":627},"Client",[444,669,610],{"class":481},[444,671,601],{"class":627},[444,673,604],{"class":481},[444,675,676],{"class":627},"UnAuthenticated",[444,678,679],{"class":481},"}\n",[444,681,682,684,687],{"class":446,"line":485},[444,683,636],{"class":575},[444,685,686],{"class":627}," MethodPutFoo",[444,688,642],{"class":481},[444,690,691,693,695,697,699,701,703,705,707,709,712,714,716,718,721],{"class":446,"line":498},[444,692,647],{"class":627},[444,694,534],{"class":491},[444,696,598],{"class":481},[444,698,601],{"class":491},[444,700,604],{"class":481},[444,702,607],{"class":491},[444,704,660],{"class":481},[444,706,601],{"class":627},[444,708,604],{"class":481},[444,710,711],{"class":627},"Staff",[444,713,610],{"class":481},[444,715,601],{"class":627},[444,717,604],{"class":481},[444,719,720],{"class":627},"Admin",[444,722,679],{"class":481},[444,724,725,728],{"class":446,"line":505},[444,726,727],{"class":575},"  default",[444,729,642],{"class":481},[444,731,732,735,737],{"class":446,"line":514},[444,733,734],{"class":627},"    err",[444,736,534],{"class":491},[444,738,739],{"class":627}," ErrUnknownMethod\n",[444,741,742],{"class":446,"line":519},[444,743,744],{"class":481},"  }\n",[444,746,747,750,753,755],{"class":446,"line":528},[444,748,749],{"class":575},"  return",[444,751,752],{"class":627}," roles",[444,754,610],{"class":481},[444,756,757],{"class":627},"err\n",[444,759,760],{"class":446,"line":540},[444,761,679],{"class":481},[134,763,764,765,768,769,772],{},"You can adjust it to your liking, but the point is to always require\nmethods to provide roles and make it obvious when a malformed route is\nrequested.\nThat ",[193,766,767],{},"default"," case is the whole story of this CVE: ",[193,770,771],{},"Foo\u002FGetFoo","\nwithout its leading slash matches nothing, so it is denied.\nThis is easy to understand, update, and test.",[134,774,775,776,604],{},"Now, if you implement multiple gRPC services, you can implement\ngeneric Unary or Stream interceptors, with a single authn function, and\na single authz function that leverages ",[193,777,778],{},"RolesForMethod()",[158,780,782],{"id":781},"phew","Phew",[134,784,785],{},"It can be hard sometimes to make the decision, should I write my own\nauth, should I stick to a library?\nFor me, this CVE landed directly in my security audit window of a\nback-end gRPC service that required authn\u002Fauthz.\nIt was one of those frightening moments to see a 9.1 CVE pop-up from\ndependabot, but delightful in realizing, you dodged it because you\nfollowed ZTA and common sense.",[787,788,789],"style",{},"html pre.shiki code .stHQG, html code.shiki .stHQG{--shiki-default:#00DAEF}html pre.shiki code .se2J0, html code.shiki .se2J0{--shiki-default:#FFD493}html pre.shiki code .sLo_3, html code.shiki .sLo_3{--shiki-default:#EEF0F9}html pre.shiki code .s8HWQ, html code.shiki .s8HWQ{--shiki-default:#ACAFFF}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 pre.shiki code .sqF6k, html code.shiki .sqF6k{--shiki-default:#54B9FF}html pre.shiki code .smONe, html code.shiki .smONe{--shiki-default:#4BF3C8;--shiki-default-font-style:italic}html pre.shiki code .sb16X, html code.shiki .sb16X{--shiki-default:#4BF3C8}",{"title":243,"searchDepth":463,"depth":463,"links":791},[792,793,794,795,796,797,798,799,800,801,802],{"id":160,"depth":463,"text":161},{"id":232,"depth":463,"text":233},{"id":252,"depth":463,"text":253},{"id":292,"depth":463,"text":293},{"id":337,"depth":463,"text":338},{"id":348,"depth":463,"text":349},{"id":380,"depth":463,"text":381},{"id":398,"depth":463,"text":399},{"id":424,"depth":463,"text":425},{"id":560,"depth":463,"text":561},{"id":781,"depth":463,"text":782},{},{"title":36,"description":70},"blog\u002Fdodging-a-critical-cve-with-zta",[72,73,62,74,75],{"sm":77,"md":78,"lg":79},"2026-10-07","QG0PvnRu0ZnJknX0yECEdANayEInm6ExC1eidvTdmF4",[811],{"id":812,"title":813,"body":814,"created":1069,"description":1070,"extension":26,"meta":1071,"navigation":501,"path":1072,"post":35,"seo":1073,"stem":1074,"tags":1075,"thumbnail":1076,"__hash__":1077},"slides\u002Fslides\u002Fdodging-a-critical-cve-with-zta.md","Trust No 1: The Middleware Bypass Pattern",{"type":131,"value":815,"toc":1059},[816,820,826,893,895,923,933,935,943,945,958,960,962,964,975,981,985,989,999,1001,1011,1030,1032,1038],[158,817,819],{"id":818},"background","Background",[134,821,822],{},[137,823],{"alt":824,"src":825},"OpenSSH","\u002Fassets\u002Fopenssh.gif",[827,828,829],"notes",{},[830,831,832,836,859,879,882],"ul",{},[833,834,835],"li",{},"SSH Bastion Server",[833,837,838,839],{},"WHY?\n",[830,840,841,844,847,850,853,856],{},[833,842,843],{},"Disgruntled TS",[833,845,846],{},"Distributed Workforce",[833,848,849],{},"Ebury rootkit",[833,851,852],{},"Conceal credentials",[833,854,855],{},"Audit",[833,857,858],{},"Chokehold",[833,860,861,862],{},"Existing products\n",[830,863,864,867,870,873,876],{},[833,865,866],{},"ContainerSSH",[833,868,869],{},"OVH Bastion",[833,871,872],{},"Teleport",[833,874,875],{},"HashiCorp Boundary",[833,877,878],{},"OPKSSH",[833,880,881],{},"All lack B2B & B2C workflows",[833,883,884,885],{},"Add support for sister companies\n",[830,886,887,890],{},[833,888,889],{},"Decouple legacy logic",[833,891,892],{},"OCI-runtime compatible, k3s\u002Fk8s",[158,894,161],{"id":160},[235,896,898,918],{"link":897},"https:\u002F\u002Fnvd.nist.gov\u002Fvuln\u002Fdetail\u002Fcve-2026-33186",[134,899,900,901,903,904,906,907,909,910,913,914,917],{},"The gRPC-Go server was too lenient in its routing logic, accepting requests where the ",[193,902,195],{}," omitted the mandatory leading slash (e.g., ",[193,905,199],{}," instead of ",[193,908,203],{},"). While the server successfully routed these requests to the correct handler, authorization interceptors (including the official ",[193,911,912],{},"grpc\u002Fauthz"," package) evaluated the raw, non-canonical path string. Consequently, \"deny\" rules defined using canonical paths (starting with ",[193,915,916],{},"\u002F",") failed to match the incoming request, allowing it to bypass the policy if a fallback \"allow\" rule was present.",[241,919,920],{"v-slot:cite":243},[134,921,922],{},"NIST NVD CVE-2026-33186",[827,924,925],{},[830,926,927,930],{},[833,928,929],{},"Middle of security review for refactor of this project",[833,931,932],{},"How did I dodge this?",[158,934,233],{"id":232},[235,936,937,939],{"link":173},[134,938,239],{},[241,940,941],{"v-slot:cite":243},[134,942,246],{},[158,944,349],{"id":348},[295,946,947,951],{},[134,948,949],{},[137,950],{"alt":356,"src":357},[241,952,953],{"v-slot:caption":243},[134,954,955],{},[144,956,356],{"href":364,"rel":957,"target":149},[148],[158,959,253],{"id":252},[134,961,256],{},[134,963,259],{},[134,965,262,966,266,968,270,970,276,973,280],{},[153,967,265],{},[153,969,269],{},[144,971,275],{"href":273,"rel":972,"target":149},[148],[153,974,279],{},[827,976,977],{},[830,978,979],{},[833,980,269],{},[158,982,984],{"id":983},"no-ticket","No Ticket",[134,986,987],{},[137,988],{"alt":285,"src":286},[827,990,991],{},[830,992,993,996],{},[833,994,995],{},"If auth happened and nobody saw it, did it really happen?",[833,997,998],{},"Trust but verify",[158,1000,293],{"id":292},[295,1002,1003,1007],{},[134,1004,1005],{},[137,1006],{"alt":301,"src":302},[241,1008,1009],{"v-slot:caption":243},[134,1010,307],{},[827,1012,1013],{},[830,1014,1015,1018,1024,1027],{},[833,1016,1017],{},"Every request passes a Policy Enforcement Point",[833,1019,1020,1021,1023],{},"The un-authenticated get a ",[153,1022,344],{},", not a bypass",[833,1025,1026],{},"Deny by default",[833,1028,1029],{},"Check again at the point of use",[158,1031,381],{"id":380},[134,1033,1034],{},[137,1035],{"alt":1036,"src":1037},"Dante's Inferno","\u002Fassets\u002Fdantes-inferno.jpg",[827,1039,1040],{},[830,1041,1042,1045,1048],{},[833,1043,1044],{},"Technical Debt",[833,1046,1047],{},"Inheritance Hell",[833,1049,1050,1051],{},"Where to assert that logic?\n",[830,1052,1053,1056],{},[833,1054,1055],{},"Point of Entry",[833,1057,1058],{},"Pint of Use",{"title":243,"searchDepth":463,"depth":463,"links":1060},[1061,1062,1063,1064,1065,1066,1067,1068],{"id":818,"depth":463,"text":819},{"id":160,"depth":463,"text":161},{"id":232,"depth":463,"text":233},{"id":348,"depth":463,"text":349},{"id":252,"depth":463,"text":253},{"id":983,"depth":463,"text":984},{"id":292,"depth":463,"text":293},{"id":380,"depth":463,"text":381},"2026-10-29","March 2026, a critical CVE in gRPC-go lead to an authorization bypass in Google’s own authz package. Why do developers keep stumbling into this pitfall?",{},"\u002Fslides\u002Fdodging-a-critical-cve-with-zta",{"title":813,"description":1070},"slides\u002Fdodging-a-critical-cve-with-zta",[74,62,73,75],"\u002Fassets\u002Fthe-x-files_md.webp","KLVL16lcDdpq8rP9iwtoI2DOCA-x40J9U4FpBhHSBRw",{"id":4,"title":5,"author":1079,"description":14,"extension":15,"meta":1080,"repository":1081,"stem":21,"__hash__":22},{"name":7,"github":8,"gitlab":9,"email":10,"linkedin":11,"mastodon":12,"bio":13},{},{"url":18,"git":19,"branch":20},1793313045135]