AGWA / git-crypt

Transparent file encryption in git
https://www.agwa.name/projects/git-crypt/
GNU General Public License v3.0
8.1k stars 472 forks source link

It doesn't handle small files (at least diff) #294

Open x-yuri opened 1 year ago

x-yuri commented 1 year ago
#!/bin/sh -eu
docker run --rm -it alpine:3.18 sh -euxc '
    apk add git git-crypt
    git config --global user.email "you@example.com"
    git config --global user.name "Your Name"
    mkdir r
    cd r
    git init
    git-crypt init
    echo "a filter=git-crypt diff=git-crypt" > .gitattributes
    git add .gitattributes && git commit -m .gitattributes
    echo a > a && git add a && git commit -m a
    git --no-pager show
'

Output:

...
+ git --no-pager show
commit c48eb1dd9139fffc07777256b8ac1b37c08c944f (HEAD -> master)
Author: Your Name <you@example.com>
Date:   Mon Jun 19 17:04:22 2023 +0000

    a

diff --git a/a b/a
new file mode 100644
index 0000000..722e550

Apparently it exits with non-zero status here. Since in.fail():

If the input sequence runs out of characters to extract (i.e., the end-of-file is reached) before n characters have been successfully read, the array pointed to by s contains all the characters read until that point, and both the eofbit and failbit flags are set for the stream.

https://cplusplus.com/reference/istream/istream/read/

#!/bin/sh -eu
docker run --rm -it alpine:3.18 sh -euxc '
    apk add git build-base openssl-dev
    git config --global user.email "you@example.com"
    git config --global user.name "Your Name"
    git clone https://github.com/AGWA/git-crypt
    cd git-crypt
    nl=$(echo -e \\nx)
    nl=${nl%x}
    sed -Ei -e "/std::cout << in.rdbuf\(\);/{iif(!in.fail()){${nl}a}${nl}}" commands.cpp
    make CXXFLAGS=-DOPENSSL_API_COMPAT=0x30000000L install
    cd /
    mkdir r
    cd r
    git init
    git-crypt init
    echo "a filter=git-crypt diff=git-crypt" > .gitattributes
    git add .gitattributes && git commit -m .gitattributes
    echo a > a && git add a && git commit -m a
    git --no-pager show
'

Output:

...
+ git --no-pager show
commit c48eb1dd9139fffc07777256b8ac1b37c08c944f (HEAD -> master)
Author: Your Name <you@example.com>
Date:   Mon Jun 19 17:04:22 2023 +0000

    a

diff --git a/a b/a
new file mode 100644
index 0000000..722e550
--- /dev/null
+++ b/a
@@ -0,0 +1 @@
+a
diff --git a/commands.cpp b/commands.cpp
index 6b3c498..5bd9584 100644
--- a/commands.cpp
+++ b/commands.cpp
@@ -937,7 +937,9 @@ int diff (int argc, const char** argv)
        if (in.gcount() != sizeof(header) || std::memcmp(header, "\0GITCRYPT\0", 10) != 0) {
                // File not encrypted - just copy it out to stdout
                std::cout.write(reinterpret_cast<char*>(header), in.gcount()); // include the bytes which we alre ady read
+               if (!in.fail()) {
                        std::cout << in.rdbuf();
+               }
                return 0;
        }

It fails with files that are less than 22 bytes long (sizeof(header)).

I'm not a cpp programmer, so there might be a more appropriate solution.