# Include\_str!("/etc/passwd")

**URL:** https://internals.rust-lang.org/t/include-str-etc-passwd/20415
**Category:** language design
**Created:** [February 29, 2024, 9:57pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415 "2024-02-29T21:57:20Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [February 29, 2024, 9:57pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/1 "2024-02-29T21:57:20Z")

</div>

Does `include_bytes!` need to support arbitrary absolute paths? (outside of workspace, .cargo, or target dir)

This seems like a surprisingly powerful feature.

I don't think crates-io dependencies have any business poking around the disk. I know dependencies already have ability to do sneaky things via `build.rs` or proc macros, but `include_bytes!` adds more things to look out for (and maybe someday the other things will be sandboxed).

In local workspaces, literal absolute paths in the source code seem like a risky practice, which makes projects dependent on disk layout or specific OS.

`include!(concat!(env!("OUT_DIR"), "foo.rs"))` is common and could be technically an absolute path, but it's not an arbitrary one.

How about adding a lint or warning for absolute paths outside of usual places like the workspace root, target dir, and .cargo/registry/src subdirs?

---

<div class="post-metadata">

### Author: ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)
#### Post date: [February 29, 2024, 11:29pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/2 "2024-02-29T23:29:42Z")

</div>

I can imagine semi-legitimate reasons why a crate might include things from `/usr/lib` or `/usr/include` (probably using a path detected or configured via some mechanism), and of course a user can pass an arbitrary path in an environment variable.

But in general, warning (in a way the crate can't suppress) for _unusual_ paths seems reasonable. This could align with whatever sandboxing system we end up using for build scripts, where the user can extend the permitted accesses from the sandbox if they expect those accesses and want to allow them.

I think the primary question is whether there's value in doing the initial step before the full sandboxing. Because in the absence of sandboxing, any crate could easily pull in a proc macro that does exactly what `include!` or `include_bytes!` does but without the sandbox.

---

<div class="post-metadata">

### Author: ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)
#### Post date: [March 1, 2024, 1:10am UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/3 "2024-03-01T01:10:00Z")

</div>

Don't focus too much on the absolute paths -- relative `../` paths can have the same effect if you try hard enough...

```rust
include_bytes!("../../../../../../../../../../../../../../../../etc/passwd")

```

Symlinks are a problem too, but I think `cargo`'s crate (tar) extraction blocks that at least.

---

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [March 1, 2024, 1:28am UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/4 "2024-03-01T01:28:12Z")

</div>

Escaping relative paths as well as symlinks can be caught as well. A check whether a path is within allowed directories will require path canonicalization anyway.

---

<div class="post-metadata">

### Author: ![Vorpal](https://avatars.discourse-cdn.com/v4/letter/v/aca169/32.png) [@Vorpal](https://internals.rust-lang.org/u/Vorpal)
#### Post date: [March 1, 2024, 6:24am UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/5 "2024-03-01T06:24:47Z")

</div>

I know we are reading in directories above the workspace at work. We have an existing legacy C++ application and have recently added some Rust. In particular I know we are including protobuf files from something like `../../proto`.

I don't remember if the mechanism is build.rs or include or whatever. But I consider that sort of thing a somewhat legitimate usage for mixed build system setups where cargo isn't the top build system. (Currently cmake fills that role, but the plan is to transition to bazel.)

---

<div class="post-metadata">

### Author: ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://internals.rust-lang.org/u/2e71828)
#### Post date: [March 1, 2024, 7:10am UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/6 "2024-03-01T07:10:04Z")

</div>

> [@josh](#):
>
> I think the primary question is whether there's value in doing the initial step before the full sandboxing. Because in the absence of sandboxing, any crate could easily pull in a proc macro that does exactly what `include!` or `include_bytes!` does but without the sandbox.

My view on this is that anything which makes the malicious stuff look weird or out-of-place is a benefit— If I'm pulling in a less popular crate, I'll at least glance over the source code to see if it feels like a quality implementation.

---

<div class="post-metadata">

### Author: ![Ltrlg](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ltrlg/32/7765_2.png) [@Ltrlg](https://internals.rust-lang.org/u/Ltrlg)
#### Post date: [March 1, 2024, 8:58am UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/7 "2024-03-01T08:58:55Z")

</div>

The implementation of this would involve Cargo (which knows how to classify paths) passing data to Rustc (which expands the macro). Unless it is done in a weird way, it should be at least possible from the outer build system to add non-warning paths using `RUSTFLAGS`.

---

<div class="post-metadata">

### Author: ![epage](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/epage/32/3171_2.png) [@epage](https://internals.rust-lang.org/u/epage)
#### Post date: [March 1, 2024, 5:13pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/8 "2024-03-01T17:13:37Z")

</div>

We have an [RFC for env variable sandboxing for rustc](https://github.com/rust-lang/rfcs/pull/2794). I think it would be interesting to also have FS sandboxing and exposing the two of these in cargo so packages declare what they need access to. I'd want to see easy opt-in for "no env except what cargo gives me" and "no paths outside the root mod's parent directory" (wording is specific so we can sandbox `#[path]`).

While there is still the hole left by `build.rs` and proc-macros (until we can sandbox those), at least we can statically determine whether those exist in the dependency tree and mark them as needing audits.

---

<div class="post-metadata">

### Author: ![cgwalters](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cgwalters/32/1065_2.png) [@cgwalters](https://internals.rust-lang.org/u/cgwalters)
#### Post date: [March 1, 2024, 7:26pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/9 "2024-03-01T19:26:53Z")

</div>

The best fix for this is using [cap\_std - Rust](https://docs.rs/cap-std/latest/cap_std/)

---

<div class="post-metadata">

### Author: ![Vorpal](https://avatars.discourse-cdn.com/v4/letter/v/aca169/32.png) [@Vorpal](https://internals.rust-lang.org/u/Vorpal)
#### Post date: [March 1, 2024, 9:25pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/10 "2024-03-01T21:25:42Z")

</div>

> [@epage](#):
>
> While there is still the hole left by `build.rs` and proc-macros (until we can sandbox those)

Is that ever realistic considering backward compatibility and things like sqlx? Same for the other types of sandboxing, it has to be opt in. Maybe you could do something with editions, but then you also need to consider that you should be able to mix editions.

---

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [March 1, 2024, 9:35pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/11 "2024-03-01T21:35:36Z")

</div>

Of course there will need to be some opt in/opt out and a migration process. For proc macros this seems to be tractable — there’s already been a WASM proof of concept, and it could automatically control file system access. `build.rs` seems much more difficult, because sys crates may legitimately need to poke around the system.

However, for me the concern is that a crate that has no macros and no `build.rs` may seem less risky, but surprisingly it still has file system access.

---

<div class="post-metadata">

### Author: ![afetisov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/afetisov/32/8508_2.png) [@afetisov](https://internals.rust-lang.org/u/afetisov)
#### Post date: [March 1, 2024, 10:32pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/12 "2024-03-01T22:32:47Z")

</div>

That makes sense. However, in that case I assume that all Rust crates and C++/protobuf/etc code live in a single folder, in some subfolders. I.e. they are not just pulled from a central registry into arbitrary places. I think it's thus a reasonable default if all crates on [crates.io](http://crates.io) were sandboxed in a way which prevents filesystem access outside of their `OUT_DIR`. It also seems like a reasonable default for crates in custom registries, because putting those in arbitrary folders and expecting some certain structure from the filesystem is likely to be a bad idea.

Also, the issue is most dire specifically for public crates on [crates.io](http://crates.io) or GitHub, rather than private registries and repositories. In those cases one can reasonably assume that only employees have access to the registry, and that they are responsible for the code they put there.

I.e. if this sandboxing is applied exclusively to [crates.io](http://crates.io) dependencies, it would be a significant improvement to security.

One interesting mixed case is where someone pulls a public potentially malicious dependency from GitHub, and opens it in an IDE, which will execute all build scripts & macros. While modern IDEs ask to enable this functionality, they are barely useful without it, so one can expect users to execute compilation scripts in the projects they open. They may do some cursory checking of build.rs or proc macro dependencies, but more complex attacks like `include_bytes!` injection could easily slip by. However, at that point the crate is on the user's filesystem, so how could we distinguish this case from the one where people are working on their own trusted crates and they need unsandboxed filesystem access?

Which makes me think that perhaps Cargo could have an override switch for sandboxing of local crates, so that all crates are sandboxed by default, but trusted projects which need unrestricted filesystem access could use it. Enabling this switch would be an obvious security flag that one could check, even automatically, to signal that the project requires potentially more scrutiny.

---

<div class="post-metadata">

### Author: ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)
#### Post date: [March 1, 2024, 10:53pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/13 "2024-03-01T22:53:40Z")

</div>

This seems like a reasonable step towards having sandboxed builds. I agree it should work in conjunction with the availability of sandboxing (or _at least_ with a lot of disclaimers to avoid giving a false sense of security). Without sandboxing, it only defeats the laziest possible attacks.

I also want to note having all of

- filespace sandboxing during build
- env sandboxing during build
- `build.rs` sandboxing
- proc macro sandboxing

will not protect anyone from a malicious dependency once they run the built executable. After the first such run, one can't assume any builds are clean (or assume much of anything else really). And I imagine most people who develop and build in an unfortified environment also run the produced binaries in that environment.

`cargo run` and friends also having some form of auditing sandbox would help some (and be worth having IMO), but would still be a best effort sort of feature as far as the produced binary goes.\[1\]

Any documentation/help around these features should make clear to the programmer that there's only so much that Cargo and the compiler can do; they can never completely protect the programmer from a malicious upstream. Those who care should be guided towards fortifying their development environments, CI best practices, `crev`, etc.

* * *

1. More sophisticated attacks will try to detect being in the sandbox, etc, so a clean audit doesn't mean a safe executable.

---

<div class="post-metadata">

### Author: ![branpk](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/branpk/32/8221_2.png) [@branpk](https://internals.rust-lang.org/u/branpk)
#### Post date: [March 1, 2024, 10:59pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/14 "2024-03-01T22:59:08Z")

</div>

Just pointing out another thing...

```rust
#[path = "/etc/passwd"]
mod foo;

```

---

<div class="post-metadata">

### Author: ![dlight](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dlight/32/8462_2.png) [@dlight](https://internals.rust-lang.org/u/dlight)
#### Post date: [March 2, 2024, 1:17am UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/15 "2024-03-02T01:17:34Z")

</div>

I think that any limitation or warning on `include_str!` should be viewed as a convenience lint and not a _security_ feature. That's because `build.rs` can do arbitrary things like running programs (such as running `cat /etc/passwd`) and there is no way to statically detect that with 100% reliability without also running the `build.rs` itself (that's called Rice's theorem). So any lint on `include_str!` can't improve the security of the build (unless one posits that malware developers will not test their code and tinker until they make it work)

Because of that, if you need to defend against a malicious code in the build step itself, if the build is not sandboxed you have already lost.

On the other hand, from the developer's point of view, this kind of lint may be sometimes annoying (there may have legitimate use cases for reading from `/etc`), so there should be a way to turn it off.

I think that not fully sandboxing the build _by default_ was a mistake. I get that some builds need to access system files (for example, to use distro-provided `-dev` libraries in `/usr/include` and `/usr/lib` rather than vendoring dependencies inside the crate itself), but there should be at least a manifest detailing every such dependency, rather than being free for all by default.

Right now the only way forward I can think of is to run the build inside a sandbox. For example, I think that something like [naersk](https://github.com/nix-community/naersk) fits the bill (but afaik it doesn't use cargo).

Maybe there should be a `cargo-sandbox` command that builds normally, but in a sandbox.

---

<div class="post-metadata">

### Author: ![Vorpal](https://avatars.discourse-cdn.com/v4/letter/v/aca169/32.png) [@Vorpal](https://internals.rust-lang.org/u/Vorpal)
#### Post date: [March 2, 2024, 8:57am UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/16 "2024-03-02T08:57:24Z")

</div>

> [@afetisov](#):
>
> . I think it's thus a reasonable default if all crates on [crates.io](http://crates.io) were sandboxed in a way which prevents filesystem access outside of their `OUT_DIR`.

If this also applied to build.rs it would break sys crates, (anything using pkg-config or calling the system C compiler at all for example) as @kornel pointed out. That is a major problem.

Other than that I think your idea seems sensible.

---

<div class="post-metadata">

### Author: ![parasyte](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/parasyte/32/8925_2.png) [@parasyte](https://internals.rust-lang.org/u/parasyte)
#### Post date: [March 2, 2024, 4:35pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/17 "2024-03-02T16:35:40Z")

</div>

My interpretation of a sandbox environment includes everything you need to build "normal" crates, and nothing that could leak private information on the host. This is a bigger scope than "just run it in WASM", but there may not be an alternative.

---

<div class="post-metadata">

### Author: ![toc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/toc/32/6692_2.png) [@toc](https://internals.rust-lang.org/u/toc)
#### Post date: [March 2, 2024, 4:51pm UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/18 "2024-03-02T16:51:32Z")

</div>

> [@Vorpal](#):
>
> If this also applied to build.rs it would break sys crates, (anything using pkg-config or calling the system C compiler at all for example) as @kornel pointed out. That is a major problem.

Emphasis then on sandboxing being a reasonable _default_. Ideally escaping the sandbox has various easy and explicit mechanisms to do so, some of which get printed out when the sandbox causes an error.

And ideally the sandbox does become the default, and not an option that simply almost everyone should enable.

---

<div class="post-metadata">

### Author: ![Valaphee](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/valaphee/32/10755_2.png) [@Valaphee](https://internals.rust-lang.org/u/Valaphee)
#### Post date: [March 19, 2024, 2:23am UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/19 "2024-03-19T02:23:34Z")

</div>

One other solution would also be to have some scanning feature, when publishing, which checks for obvious things and makes it visible to the user.

For example using base64 encoded strings, could generate a warning, as its not that typical to use in a library. Or any form of /etc/passwd, generates a severe warning, etc.

I definitely prefer this approach over sandboxing, because as soon as you invoke the compiler, which is typical for build.rs, you can do everything anyway.

And an easier way to report crates with malicious intend might also be a good idea. (Which I guess is not implemented yet for spam reasons)

---

<div class="post-metadata">

### Author: ![mathstuf](https://avatars.discourse-cdn.com/v4/letter/m/958977/32.png) [@mathstuf](https://internals.rust-lang.org/u/mathstuf)
#### Post date: [March 19, 2024, 11:03am UTC](https://internals.rust-lang.org/t/include-str-etc-passwd/20415/20 "2024-03-19T11:03:29Z")

</div>

> [@Valaphee](#):
>
> base64 encoded strings

Test suites may contain them. Docstrings for examples of things using Base64 (HTTP header examples come to mind) may exist as well.

> [@Valaphee](#):
>
> Or any form of /etc/passwd

Again, test suites and comments may exist. And anything simple is going to miss `concat!("/etc", "/passwd")`.

[Next page](https://internals.rust-lang.org/t/include-str-etc-passwd/20415.md?page=2)
