 Hey, everybody. Thanks for coming. Yeah, thanks for walking all through. Pretty far down the hallway, so thanks for coming on Friday. Really appreciate that. So today, we'll be talking about from security testing to deployment in a single PR. We'll be covering a lot of different things of how you can start a lot of your automation and incorporating security earlier on. Just for quick introductions, I'm Sarah Khalife. I'm a solutions engineer at GitHub. Previously, I was working on a lot of cloud-native, cloud-platform kind of deployments, worked on a lot of microservices, but now over at the GitHub side. What else do I have here? For fun, I enjoy playing volleyball, traveling, and so happy to be here in Detroit and getting to check out Detroit this time around. And I'm Grant Griffiths. I'm a member. I've been at Polarworks for four years now. Previously, I was at GE working on the platform team over there, also a contributor to SIG Storage and Kubernetes CSI. So I'm involved in the Kubernetes community. And for fun, I like to go climbing, surfing, and trail running. That's actually it. All right, so today, we'll be talking about our one. Sorry, can't do that. It's OK, we can just use the one. Today, we'll be talking about our agenda here. So we'll be starting off with our introductions and an overview of why we're even doing this, what are some of our motivations. We'll be covering our security scanning, our automation and CI, and then we'll actually jump into a live demo. So it's not just slides, it's more of a demo. And we think that's more important to show it live as we're working through it. And then we'll finish it off with some of the takeaways and benefits of some of the processes that we're following. All right, first introduction and overview. So actually, previously in 2020, we did a session at KubeCon Europe 2020 around automating with Kubernetes and Docker, so kind, and building out your CI pipeline in a single PR. So we talked about what is kind. We talked about how you can actually trigger multiple versions of Kubernetes using kind to run all of your testing. Because we know not one application is going to run on the same environment in every deployment that there is. So this year, we're kind of taking that to the next level. We talked about automation in the PR, but now we're taking automation and including security into it. So I'll come over here really quick. So some of the things we'll be covering today, we'll be talking about some of the simple steps to automate our build process. We'll be leveraging a lot of the open source capabilities as well as open source tooling and free tooling to run these different types of scans that we're going to be talking about. And at the end of the day, we're automating the containerization and triggering all the things through just the pull request and some of the other types of automations that you could do. We can easily talk about those. But we want to make sure that anything you do can be done before you even merge your code into your production environments. Our goals are to run integration testing and security testing within the single PR. We want to make sure that you're detecting vulnerabilities way earlier on and you're fixing them because we're actually enforcing some strategies, some testing, some validations before you even get into your production branch. So we want to enforce that and block the merge if there are vulnerabilities. We are configuring branch protection rules. So the goal with branch protection rules is to make sure things are more transparent. We understand that not everybody has the same rules across the board on how they're doing their deployments and how they're doing their management of their application and their automation. So by configuring the different branch protection rules, at least we're setting that standard and setting a standard that makes it more transparent of what the requirements are to go into a production environment. And at the end of the day, we're talking about automation. Nobody likes doing anything manual, especially when it's mundane and continuous, things that you have to do all the time. So why not just automate it so you don't have to think about it when you're working through your actual things that you need to work on? So what's our motivation? So I mean, I'm sure you've all heard of a lot of the recent exploits. The lock for J1 was a pretty big one. The Heartbleed Bug and the Solarigate ones were some of the major ones that I had to deal with for my day to day work the last couple of years. So going back and fixing a lot of these issues can be very tough. It's never a fun process to go fix a security vulnerability. I mean, in many cases, nobody says security scanning is fun. But it does cause a lot of friction, because a lot of times you're working either as a developer or as a security researcher, and you have to kind of work with both of the teams. But there is never an easy and collaborative process when you're working through these issues. The other thing is we've actually done some research. And what I thought was really interesting is that Splunk did a state of security. And what was really interesting for that, they said 65% of our organizations are reporting an increase in attentive cyber attacks. So I think that's a pretty big number when you think about it. And actually what was really great, it was reinforced when I was listening to the first day keynotes on Wednesday, slim.ai did a lot of research, and they created a lot of reporting as well. And they noticed that more high and critical vulnerabilities than ever before, especially in the containerization world, in the cloud native world. So we wanted to make sure that anything that we're doing is secure from the beginning. Cool. So today we're going to be talking about the entire CNCF ecosystem. So we're going to cover every single box. No, I'm kidding. But we're going to be focusing on a couple tools here in the CNCF security ecosystem specifically. So we'll be demoing how we can incorporate these into the build process easily with GitHub Actions. So there's a lot of different types of security scanning. There's image scanning, where you kind of build an image and scan it. There's dependency checks. So a lot of code has like, basically it's very easy to scan and see what kind of dependencies you're using your application. And you can cross check that against like a known list of CVEs, security database, and check to see if you're using any dependencies in your program that are vulnerable. Then there's also static code analysis. So this is kind of like the intelligent tool that we'll go through and read your code and check to see, am I doing something like a SQL injection? Or am I vulnerable to cross-site scripting or something like that? And then there's configuration checks. So it's very easy to make a mistake these days with infrastructure as code. You may be like googling on how to set up your Terraform application, how to deploy a database with Terraform. And you might just set SSL to false. Or you may run your database in insecure mode and expose it to the internet. And these kind of things are very easy to check with these kind of security scanning tools. But if not, you could be the next company in the news that's hit by a big hack. So yeah, we're going to go through a typical workflow. This is kind of what a typical workflow looks like for a developer working on a cloud native application. So your developer introduces a code change. Maybe they're working on a feature to add a new database to your application. This will immediately kick off typically like a build job. So make sure your code compiles. It'll run all your tests. Make sure a test is all good. You'll get a review from one of your teammates. And then once it's all good, you'll go ahead and you merge your code. And then you do this over and over. And finally, once everyone's fixed all of their issues or fixed all of their features or implemented everything, it's code complete. So it's dev complete. And everyone's excited to start releasing. But then QA team comes along or security scanning comes along, maybe late in the release process. You might just kind of run your security scans. Maybe there's a team that does it, or maybe it's someone else. And then, uh-oh, the scan failed. So you're right about to release. And now your scan has failed. And now you need to file an issue and assign it to the development team. Now the development team, of course, is our developer in the bottom right. They have to go back and they have to go and fix all of these bugs and vulnerabilities. They may have found out now that they're using a bad SQL server, or they're using a bad image, or they're using a client that's out of date, or something like that. And then they have to update that client. And then maybe there's API changes with that client that they need to use or integrate with. So very easily, you can see, you start to push out the deadline further and further out. And no one's happy about that. This is the kind of improved workflow that we're going to talk about today and how you can utilize a lot of the different CNCF tooling out there to kind of improve your build process. So it's the same as before, you introduce a change, you have a build job. But then what's nice is then you can do vulnerability scanning inside of your pull request. So there's a lot of different tools out there in the CNCF ecosystem, like I mentioned. But today we're going to be talking about just a couple of them. So we're going to talk about Trivi from AquaSec, I believe. And then TFSec, as well as CodeQL and Dependabot. You can go check out these companies at QCon, as well as the rest of the ones in the security ecosystem that I showed earlier. And all of these are? Yeah, all of them are open source and free. Yeah, and then after all of your code scanning finishes running and you get your pull request review and then your tests are passing, you know that when you go to release, there's not going to be any vulnerabilities because you've already been checking all along instead of at the end of the release. Cool, so now we're going to get into a live demo. We just want to kind of set the stage first. So we're a demo project that we're going to do is a project that I use at work day to day, which is STORC, which is an open source project by PORX. And it has a Kubernetes schedule plug-in to snapshots and object service controller open source. So go check it out if you want. But that's the demo project that we're going to be using today. And what we're going to demo is a couple open pull requests. We're going to show a couple failed builds, as well as branch protection rules on how you can prevent those from being merged, and then as well as we're going to show a successful PR and how it all fits together. Cool, so first thing, I think Sarah's going to kick this one off and we'll go through this change. All right, thanks, Grant. So as Grant mentioned, we're kind of working through the pull request. I think this is where we can actually open up a lot of the conversation. You're pretty much thinking through, like, hey, my feature is complete. Let me create a PR and add new code. For example, here I'm actually creating a PR. So I'll just kick this off, where I'm adding a new set of a new database. So I'm creating pull requests to create a new database using Terraform and kick off that pull request. So as I'm kicking this off, what you will see here, we're automatically triggering the different jobs that we're mentioning. We're assuming, oh, is that better? We're already kicking off a lot of our deployment jobs or a lot of our integration jobs and a lot of our security scans. So what's really important, most people think, like, let's kick off our integration scanning. But why not incorporate some of the security scans right then and there? In this case here, my database, hopefully, will show that it's invulnerable because it's not good code. I copied it and pasted it from probably somewhere bad, somewhere on Stack Overflow. And it triggered, oh, actually, it worked pretty fast. So it triggered our automation to run at the pull request level and automatically detect that, hey, you have the vulnerable creation of this Terraform script that you're creating to create a database. You can see the database connection and application. Sorry, database connection where applicable. There's a couple of different other ones that just kick off writing your pull request. So what's really nice about this? Now, you, as a developer, you might not understand what does this mean. You can go and have that conversation with your teammates that might have gone through this before. You can go bring in your security researcher and say, hey, can you tag them in and say, hey, can you help me understand what's going on? I don't really understand why this is vulnerable. So this makes it so much easier. And this is where you see feedback as a developer. So what we've done really is kick off these automations through the pull request. And how do we enforce them, what do? So in this example here, you can do this with other tools as well. But what I've done specifically is set branch protection rules. So I'll actually just jump into what those look like. So I've set branch protection rules that require a couple of different things. I'm requiring a pull request to happen for the pull request to kind of do the review before I merge anything into my production. What I've also done, I've required a couple of status. It is 100% the easiest way to enforce a lot of these settings that you want, or a lot of the scans that you want at the pull request level to make it easier for a developer to know what's expected. So if you're expected to be able to build your job every time you create a PR, if you're expected to run a production test successfully, that's great. But why not integrate some of the security scans there? If you're expected to do those anyway before you go into production, right? Sorry, I think the mic's going in now. Great. So what I've done, I set a couple different things here. So I'll go back into not only what we've done to create this, but how did we do this? In this example here, let me pull up. There we go. In this example here, I'm using GitHub Actions. It's open source actions that are created by a lot of other vendors, not just GitHub themselves. So it makes it easier to understand what's going on in that code base. So what we've done, we actually look through some of the capabilities that we can pull in from the cloud native community. So Aqua Security was creating some actions to do the TFSEC scans, to do their trivia scans. So what we've done, we've kind of taken that and built it out into a CodeQL slash trivia slash dependency scan all in one workflow. So you don't have to go through and find all the different tools that you have to work with together. You can just see them all in one security YAML tool, but security YAML workflow. So in this example here, we're kicking off our CodeQL. CodeQL does a code analysis of the code and it understands from source to sync where the vulnerability is. We are doing a, if we go to the next section here. Oh, let me zoom in again, yep. We're doing a trivia scan, so we're scanning here not only for vulnerabilities across the board, but specifically vulnerabilities associated to an image. So what we're doing, we're building the image and then scanning it at the time of the PR. And then with TFSEC, what you saw earlier with the vulnerabilities being posted right in the pull request is what we're doing is scanning terraform or infrastructure as code in general with TFSEC to see these results in the pull request once more. So all of these, and actually one more that we're actually enforcing is dependency review. So we want to ensure that any dependencies coming in are also secure. So I'll pass over to Grant to kind of walk through some of the other example pull requests that we've created so you can see the vulnerabilities there. Cool, so let's go back to our pull request here. So that was one example of a pull request, but we actually have a couple open that we wanted to go through as well that are already failing. So let's say we wanna add port and stork to basically check for IDs maybe in webhook.go. And for some reason we wanna store these in a SQL database or maybe query our database to see if we've hit a request ID like something like that. So this is just a simple PR. What it's doing is we have a go file webhook.go and what we're doing is basically importing the database slash SQL package as well as just setting up a database. So what we're doing here, we're just creating a new connection to a database, checking if it passed and closing the connection later on. But what we're doing actually is we're checking the ID based on the request that comes into this function. So you can go back up here all the way the top. What this is is actually, it's actually a HTTP request in the header right here. So we're taking input from the HTTP request and it's kinda hard to kinda see that through GitHub typically but what's nice is that inside with this CodeQL action, what you can do is actually check here and see this is where the query is happening. So what's happening basically is that we have a SQL injection vulnerability here. Okay, so yeah, so this is the CodeQL kind of vulnerability that we found. So with CodeQL, it's basically the tech, it's gone through all of our code, all of our Go code and it's kinda scanned through everything. It's basically set it in a format in CodeQL where it knows how to kinda interpret this data and then it cross checks in a different way to basically see if it's one of the known vulnerabilities in Go. And in this case, what it's found is that it's a SQL injection, so yeah. And then I think Sarah wanted to show something as well. Sorry, yeah, so I just wanted to show a couple of different things. So when you click on show paths, in this step here, you'll see the source, which is where it's being introduced all the way down to the sync where it's being exploited. In this case, there's only two steps but in many, many other cases, you'll see maybe 10 steps, 15 steps, 20 steps. So that's here for you as a developer to understand, okay, maybe I should be doing sanitization in step number one, step number two, step number 20. So it makes it, again, easier for you to understand where to make that fix. But in reality, I mean, in many cases, when you first see the vulnerability alert, you see database query built from user-controlled sources. Who understands what that means as a developer? I mean, I kind of understood but I really didn't know. You make it, it's not an easy thing to understand right off the bat until you see a lot of security teams will come in and say, hey, you have a SQL injection. Okay, cool, I understand that, but what does that really, really mean? So when we walk through the analysis, we actually see that the SQL injection, what is an example of the SQL injection? What are some of the recommendations? And what are some good and bad examples? So then, again, as a developer, you're not only figuring out where to make the fix but you're learning through it so you're not making that mistake again. Sorry, just wanted to add that. Yeah, so as Sarah mentioned, that's kind of the difference between myself, like I opened the pull request, I'm familiar with that function, but whoever's reviewing that pull request might not know that in the beginning of that function, we're taking a user input from an HTTP request. So it's really just taking the human out of the equation and humans are vulnerable to mistakes and robots are a lot less. So, cool, so that's the second one I guess we'll show. The next one that I wanna show is, let's do something harmless, let's add Swagger support to our application. You might think, oh, I saw this example on a blog, maybe someone said you should add Swagger support so they kind of gave a code snippet on how to add Swagger support, which is a very common thing to do, developers, everyone knows we just copy code and then put it in the application and make it work a lot of times. So everyone's definitely done that. And so this is an example of what that might look like. So we're importing something called Swago slash HTTP Swagger on GitHub. And we're just setting up Swagger, we're adding a new mux, a new router, we're setting up the URL rel to handle the Swagger requests, and then we're just starting a new Swagger server. But the problem here is that we're actually importing a invalid version of Swagger that has a vulnerability. So actually, if you go back to the pull request, let me zoom in again, you can actually go down and you can see that one of our dependency checks failed. So it checked for a dependency review and you can see right here that it failed. So if we go ahead and check that, yeah, we can see that the dependency review failed but we can also go back. And so now we've seen that our dependencies failed. So what we're gonna do is go over to our go.mod and we can open up the rich text view and we can see that there is actually a denial of service of vulnerability in the version of Swagger that we're importing. So it might be harmless to kind of expose Swagger on your API, but if someone knows and they scan it and they see they're just scanning Swagger endpoints as a hacker, they could do a denial of service on your Swagger. And if that Swagger is running on your API server, now you no longer can serve API requests in your application. So that could be, something that's very harmless is adding Swagger support can be, can add a denial of service. So that's one thing we, that's another thing we wanted to show. The next one is let's go ahead and let's change our base image. So as a developer, the nice thing about Docker is that we can choose whatever base image we want. A lot of times we'll pick, whatever base image is kind of the easiest to kind of run our application. So the pull request is, it's just a cleanup PR real easy. As discussed, let's switch to a Ubuntu based image. It's way easier to use, right? But the problem with this is that we're actually importing a version of Ubuntu that has some vulnerabilities in it. So let's go ahead and see what the vulnerability scan was. So in this case, it actually passed. But the reason for that is that the vulnerabilities that came up medium and low. So there were medium and low vulnerabilities. So we still let it pass. But it's still important to know what all of the different security vulnerabilities are when you're kind of doing this. But let's say you switched to a base image and there were high vulnerabilities in that. Maybe it was an unpatched version of an OS. Maybe it was a less known kind of base image that had a lot of problems. You could see how a case like this could very easily make your Docker image vulnerable to attacks. But let's quickly go ahead and show that this one, there's 15 new alerts. So in this case, it didn't fail the build because they're only medium and low. So we've set image, we've set that build process to only fail if it's a higher critical vulnerability. But it's still good to know that all of these CDs are in the image that we've seen below. As you can see, yeah, there's a lot of problems with the image, the trivia is detected, as well as code scanning. So yeah, so this PR introduced that. Cool, so I think that was one of the last ones. Let me go back and check. Cool, so yeah, those were all of the examples that we wanted to show. So we also have some dependable PRs open as well. Basically you can set up dependable in your PR, in your code to kind of automatically update your dependencies, which is a best practice. So always stay up at the latest, and yeah, so. All right, so what we just demonstrated are four different pull requests that were triggered just with one YAML file, like one automation. We didn't have to do too much work to enable this. This was just a great example of how you can just automate a lot of these pull requests, or automate a lot of these scanning within the pull requests by just adding one or two or three components within your security scans in the time of the pull request. So what's really nice about this is when we go back into our YAML files here, you don't have to do this only with GitHub, but with GitHub Actions, you can just kick it off right off the bat too. So you can do this with Jenkins because I'm sure a lot of your pull requests require maybe a status check on Jenkins or if you're using CircleCI or TravisCI and so forth. But what's really nice is that it makes it easier to understand again, what's required, what do you need? And the requirement is no secure, no vulnerable code in your code base. What's the threshold, medium, high, low? Can I push some code with medium vulnerabilities? Your security team might say yes. But at all times, when has, as you as a developer, when have you actually thought of integrating security earlier on? Have you done that? And raise your hand. If you have, incorporate some of your security scans. That's great. How often do you work with your security team to understand what these vulnerabilities are? Okay, a little less. So what does that mean? It means it's not always a shared responsibility. So at the point of time, you have a lot of vulnerabilities set up and you're getting all these vulnerabilities. You may be getting like 1,000 vulnerabilities all in one place. But how do you make them, how do you only see them but actually fix them? That's the crux here. Like how do you actually get to fix the vulnerabilities? So, if we go back to our slides here. And what we actually showed was a successful workflow, the scans running at that time and merging the code. But some of the takeaways are, what we were able to do is actually fix these vulnerabilities with the right context at the right time. If you're actually running the scans on the PR, what you probably want to do is to only scan the new code changes that you're introducing. You don't want to scan the whole code base every single time and give a thousand responses around, okay, this vulnerability might have existed from this time long ago. So what you want to do is maybe try to scope down what all the new changes that you're making. So then that makes it easier for you to process the results from the pull request. So if you have a couple of results being able to understand what those results are, that means you are able to fix those results. So within a couple of minutes or a couple of hours within one pull request, you're able to have the right context and have the information at the right time to make those fixes. So our goal here today was to show how you can do continuous integration, but incorporating security, making those fixes automatically right off the bat as the pull request comes in. We want to make sure that there are some branch protection rules or some rules that will allow you to enforce certain standards. Because when you enforce those standards, it makes it more transparent what the requirements are and it also makes it easier to get going and not just being blocked on certain things. And at the end of the day, we want to make it easy. So doing it at the pull request, it's right when a developer such as ourselves wants to contribute code back and is ready to receive feedback. But that's when we also want to incorporate security to add that feedback in the pull request. So now you not only make it easier to collaborate and have that conversation with the security and the developer teams, but if somebody is new to the team and doesn't know how it was done before, they can go back into the pull request and see where it happened in the previous session. So then they can learn from that so they can always reference back to it. And with that, I think we have a couple of minutes left. So let's move over to Q&A. Thank you. Yeah, thanks for the talk. Actually, I have a question regarding, from your experience, how many false positives have you been seeing with these checks and how configurable are they in terms of the transparency as a platform developer or a security engineer? Like can I just pick and choose like what are all the checks they can do, et cetera? Great questions. Very configurable, the answer. So the question is how easy is it to configure if you don't want to run all the checks and if there's a lot of false positives? So when we're talking about CodeQL in this example here, let me pull that up and just go back into our pull request, it's actually very easy. Most of the capabilities are, most of the actions and the flows are open source. So what you can see here, for example, into the example here, when you see, oops, sorry, doing it on a different laptop is a little bit harder. When you see the results here, you can actually jump in and view the source. So CodeQL queries are open source, they're developed not only by GitHub, by Microsoft, by Uber, by Google, and so forth. So these queries are open source so you can actually take them and customize them. So when you run these, you run certain query packs and you can choose what query packs you want to run. And if you want to add capabilities or make fixes to the queries, then you can do so easily. Yeah, individual actions, they also have a lot of configurations in the YAML that you can change too. So in the code, but also they all like expose different ways to do scanning. So we've configured one of the alerts to only fail on high and critical. So that's an example of a configuration that we did. Any other questions? Yeah, so these are all great right here. So these are all great tools, but how would we find out if whether a tool is doing more than it should be? Like it's actually taking our code and sending it somewhere. That's a good question. So it depends on what you're using. If you're using something like actions, you can monitor exactly where your code is being managed and where it's kind of being pushed. You can monitor the ingress and egress of the actual VM or runner that you're using. From the perspective of what's going on in the actual action itself, like the actual components, all of them are open source. So you can actually run validations and secure scanning against it. For example, a lot of, if we're talking specifically about actions, a lot of the actions are built in, y'all go into the open source, one of the TFSec one, for instance, or the trivia one here. You can actually see exactly what's going on. So if you go into this trivia action here, if you go to github.com and paste the action's name, you can actually see, you could see exactly what's going on. I think that's the, again, power of community is that you can, it's not a black box. You can go into it and see it. And then if you're monitoring of your runners, these are all running in a CI. So if you're doing some monitoring of your runners, you can see all the information flowing in and out of those runners, depending if you're using like Jenkins or Javis or so, sorry. These actions here that we're using, we're actually pulling them specifically from just common knowledge. I know Aqua Security's a company that is doing a lot of these capabilities. So I go to the Aqua Security side and I get the action. I might not go get an action from somebody that I don't know if it's an individual user without me doing my own scanning and validation of that action. Do we have a microphone? Yeah. So I can see in the pull request of the, lots of the checks was running, but it's not related to the code chain. Yeah, so is that possible to like only check what the vulnerability of the code that have been changed? Is that checking on the entire report? Yeah, I know specifically the CodeQL one, I think you can definitely check. And only to do run the check against like certain packages that have been changed. So I know that's definitely configurable with that one specifically, but we'd have to check on all the individual actions and see if they support that. I think, yeah, we've had another question. Yeah, hey, so in our org, we do a lot of this already. So we have this as the build NPR level. You got your all sorts of scanning. So it's very familiar to me, but as the organization scales and as we add more integration and more scanning, naturally, the more smoothly and perfectly everything runs, right? So it creates a load on our DevOps teams, particularly myself, to deal with all the failures that do happen and developers aren't immediately familiar with all these checks, right? So where my head is at is I'm thinking about shifting that even farther left. So you have like your ID extensions for like SonarCube, SonarLint, SNCC, and then using Git hooks as well. Have you guys thought about that? Have you guys thought about shifting that even more left so developers get a quicker feedback loop? That's a wonderful question. Yes, we have thought about it. I think that there are some points in time where that might be helpful. What we've seen is that developers usually get frustrated when they're getting alerts in their ID, when they're trying to kind of go with their flow. Like they're trying to think through their feature, adding new capabilities, and they just, they're not ready to receive a lot of the feedback. So a lot of times it adds more friction, especially if it's false positives. If you're a hundred percent, and they're gonna be very high accuracy, then yeah, it's fine. But if there's a chance of false positives while they're working that ID, it's been very, it's a very frustrating process to just kind of think through application development and incorporating changes right off the bat. So at the poor request is when we saw there's a lot more acceptability to receive feedback. And at that point of feedback, your cave, like some of it is, because there's never gonna be a hundred percent accuracy in all the things that you run. So if there's any chance of a false positive then running in the ID, it's not worth it. But at the poor request, it's a little more flexible because people are ready to receive feedback and then they can follow a process that you can automate some of the processes right after that if it is a false positive. Pre-commit works for an extent of time, right? Pre-commit works when it's high vulnerabilities or some other type of like critical ones. Not necessarily at medium or low because if you do a pre-commit, you're blocking every single time. Blocking a developer from, maybe it's not a vulnerability and maybe it is, but maybe the developer is working on like midnight versus working at like 6 a.m. when maybe the security team is not there to discuss it. So doing a pre-commit means that the developer never pushes their code into your environment. So pre-commit works to an extent. I think what works for pre-commit is if you're doing secret scanning. If you're scanning for secrets and you don't wanna expose any secrets, that's when pre-commit works. But for code scanning specifically and vulnerability scanning, it doesn't always. Especially when you talk about image scanning and infrastructure as code scanning, that's definitely not a pre-commit thing in my mind. Any more questions? Awesome. What we also did is made this public. So you can go see what we've done in our code base. And if you have any feedback to our presentation, I know a lot of people have left already, but if you have any feedback for a presentation, feel free to scan the QR code. I'm not sure if you can see it anymore, but anyway, thanks, everybody. Thank you. Thank you. Thank you. Thank you.