Zat is (in the distant future) meant to automatically discover potential vulnerabilities for you Zig packages (e.g. published via EUVD or via custom advisory databases similar to RustSec). For now, it is at least useful for generating CycloneDX SBOMs, Mermaid dependency graphs and publishing dependencies via the Github dependency publishing API. It currently gathers information from a) the build.zig.zon , resolving transitive dependencies, and b) from build.zig by injecting code into the build.zig and then running zig build -h to create the build graph, which is then parsed.
I dont see any reason you cant use a custom build runner instead of injecting code, it would feel much less sketchy if you did that.
you can find the default build runner in [zig lib dir]/compiler/build_runner.zig its just the root of an executable, the root build.zig and build.zig.zon are provided as imports.
you can get the zig lib dir by running zig env
you can mostly copy that and then inspect the graph instead of compiling the code.
For usability it would probably better to have a custom build runner that eliminates the requirement of having Zig installed but I wouldn’t consider this approach more sketchy than using zls (which modifies your files permanently by formatting them), using any third party package you haven’t audited etc. I’d consider it more sketchy that zat downloads dependencies. If you use VCS or calculate a hash over the build.zig you see that the file isn’t modified permanently.
That would require making your own zig compiler, since the build system is just zig.
zls just calls zig fmt, whose entire purpose is to format zig code, it is expected and trusted to modify the source text in a particular way. You see the changes and can quickly verify it didn’t do anything nefarious.
ZAT is a tool to detect use of packages that have known vulnerabilities. While you do explicitly state it injects code to inspect the build graph, I would have assumed it just inspects build.zig.zon of root and dependencies. You do not see the injected code unless you read the source so you can’t easily verify as it is reverted. The biggest problem is that it is executed.
That argument applies to a custom build runner as well, however it is less invasive to use the ability provided by the compiler rather than modify my code.
You are correct that it’s no less sketchy than having a dependency. There are plans to have build.zig and the runner in a Wasm sandbox in the future, reducing the risk, though not eliminating it.
Regardless of if you inject code or use a custom runner, I would expect this behaviour to be behind a flag. Especially since your existing use cases don’t seem to use the granularity provided by inspecting the build graph, nothing that couldn’t be gotten with build.zig.zon.
you can’t easily verify as it is reverted
Of course you can: git status
The point is, if you use any third party application or library you have two options a) read through the whole code (which is not feasible in most cases) b) trust the author and hope that malicious code will be detected by you or others before it affects you. Trust due to having no other option is the main reason why supply chain attacks work, as you stated “it is expected and trusted to modify the source text in a particular way”.
There are plans to have
build.zigand the runner in a Wasm sandbox in the future, reducing the risk though not eliminating it.
This would reduce the risk of executing third party code during the build process but doesn’t protect you from malicious dependencies.
Especially since your existing use cases don’t seem to use the granularity provided by inspecting the build graph, nothing that couldn’t be gotten with
build.zig.zon.
What do you mean? How would I extract information about the components (modules, libraries, executables, …) from the build.zig.zon?
Generally, I understand your point that there is always risk involved in running an application that makes any modifications to the (file)system. The only way to assure that no unwanted modifications are made by a third party application is a) audit the whole code (mostly unfeasible) b) run it in a sand boxed environment. This is why things like Snap exist.
If you worry that I mean you harm then calling zig build --build-file build.zat.zig -h with a modified build.zig should be the least of your concerns. I could try to download malicious code and execute it as soon as you execute zat, I could start removing or modifying arbitrary files, etc. In that case you should always run the application in a sand-boxed environment.
i was not talking about verifying it is reverted, i am talking about verifying what the injected code is, which is important as it is run.
you are correct that realistically people will trust their tools, many of which carry the same risks as this one.
I am not so concerned about mitigating the risk, I am concerned about respecting the users, doing so makes your tool more trustworthy anyway.
I was talking about the risk in your injected code/potential custom build runner.
you can look at the root projects dependencies, you can look at dependencies dependencies through the cache, the hash is the name of a directy somwhere in there (i cant be bothered to find it rn). you dont need to know the exact modules/artifacts being used, you can just flag packages that have a known vulnerability.
I dont think you mean harm, I do think your tool doesnt sufficiently respect the user as it enables an invasive feature by default, especially since the intended users are developers/auditors who are knowledgable enough to care about this sort of thing.
Your tool is a guest on the users system, it should act like it.
The only way for dependencies to be respectful in this regard is a compartmentalised api counts, but that’s good for other more practical reasons so should be strived for anyway.
I am concerned about respecting the users
This is a very philosophical approach/ debate as “respecting the user” can mean different things for different individuals.
In general, executing any application on your system is invasive as it alters its state and this is usually the reason why you execute a application. The question is, does executing an application affect you in a negative/ unintended way. This is not easily answered as it requires you to review the application and all its dependencies. If you don’t do that you have to fall back to trust.
If you want to review the injected code you can do so as the application is open source. But again, if you worry about a modified build.zig you especially have to worry about the application as I as a malicious actor have more control of the application as I have over the Zig compiler.
But you’re right. If your compiler runs as root and the build.zig is executed as root then you can probably do some nasty stuff. But this is then more a issue with the compiler itself.
you dont need to know the exact modules/artifacts being used
Listen, you’re probably a nice guy but as we speak about respect, I find it a little bit rude that you project some single use case onto my project and then tell me what I should do.
I don’t force you or anybody else to use the project. Maybe I’ll add a custom build runner in the future, maybe I’ll stop working on the project in a couple of months, who knows.
yea that was hypocritical of me, sorry.
I don’t intend to force my opinion on you, this started as me not knowing if you knew that you could do this with a custom build runner.
This is supposed to be you showing off that cool thing you did, which is in fact a cool thing you did, not a debate.
This is a cool project. Not only for security, just seeing the dependency graph of all the packages used is very useful. Kudos for making the release.
This is a neat idea, look forward to seeing where it goes.
If I’m reading it right, it doesn’t so much “inject code” as it rewrites and makes a copy of the build.zig, then runs that, right? So it doesn’t really modify user code at all, at no point does the master build.zig change, correct?
I’m not seeing how this could be a problem, sorry.
It might be nice to stick the temporary zat’ed build file in zig-out though. That directory usually gets .gitignored so even if something weird happens, the code-injected build file probably won’t get checked in. Or use /tmp?
If I were going to worry about anything, it would be the usually-safe assumption that no fn buil_ exists in the build file. Maybe worth generating a random nonce and using that instead? May as well raise it from “who would call a function buil_ in a build.zig, what a weird thing that would be” to “there are not enough seconds in the expected lifetime of the Universe for this to get collided, even if we turn everything into computers and just run ZAT”.

