Skip to content

Commit 1560614

Browse files
committed
Do some parsing of git hashes based on length.
Note that for Nix-native information we should *not* do length tricks, but instead always rely on an explicit algorithm. This hack should be only for foreign hash literals.
1 parent 6da56d7 commit 1560614

File tree

6 files changed

+40
-9
lines changed

6 files changed

+40
-9
lines changed

src/libfetchers/attrs.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "nix/fetchers/attrs.hh"
22
#include "nix/fetchers/fetchers.hh"
3+
#include "nix/fetchers/git-utils.hh"
34

45
#include <nlohmann/json.hpp>
56

@@ -111,7 +112,7 @@ StringMap attrsToQuery(const Attrs & attrs)
111112

112113
Hash getRevAttr(const Attrs & attrs, const std::string & name)
113114
{
114-
return Hash::parseAny(getStrAttr(attrs, name), HashAlgorithm::SHA1);
115+
return parseGitHash(getStrAttr(attrs, name));
115116
}
116117

117118
} // namespace nix::fetchers

src/libfetchers/fetchers.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,9 @@ std::optional<Hash> Input::getRev() const
447447
} catch (BadHash & e) {
448448
// Default to sha1 for backwards compatibility with existing
449449
// usages (e.g. `builtins.fetchTree` calls or flake inputs).
450+
//
451+
// Note that means that for SHA-256 git repos, prefixing
452+
// must be used.
450453
hash = Hash::parseAny(*s, HashAlgorithm::SHA1);
451454
}
452455
}

src/libfetchers/git-utils.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,4 +1404,21 @@ bool isLegalRefName(const std::string & refName)
14041404
return false;
14051405
}
14061406

1407+
Hash parseGitHash(std::string_view hashStr)
1408+
{
1409+
HashAlgorithm algo;
1410+
switch (hashStr.size()) {
1411+
case 40:
1412+
algo = HashAlgorithm::SHA1;
1413+
break;
1414+
case 64:
1415+
algo = HashAlgorithm::SHA256;
1416+
break;
1417+
default:
1418+
throw Error(
1419+
"invalid git hash '%s': expected 40 (SHA1) or 64 (SHA256) hex characters, got %d", hashStr, hashStr.size());
1420+
}
1421+
return Hash::parseNonSRIUnprefixed(hashStr, algo);
1422+
}
1423+
14071424
} // namespace nix

src/libfetchers/github.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ struct GitArchiveInputScheme : InputScheme
4848
auto size = path.size();
4949
if (size == 3) {
5050
if (std::regex_match(path[2], revRegex))
51-
rev = Hash::parseAny(path[2], HashAlgorithm::SHA1);
51+
rev = parseGitHash(path[2]);
5252
else if (isLegalRefName(path[2]))
5353
ref = path[2];
5454
else
@@ -74,7 +74,7 @@ struct GitArchiveInputScheme : InputScheme
7474
if (name == "rev") {
7575
if (rev)
7676
throw BadURL("URL '%s' contains multiple commit hashes", url);
77-
rev = Hash::parseAny(value, HashAlgorithm::SHA1);
77+
rev = parseGitHash(value);
7878
} else if (name == "ref") {
7979
if (!isLegalRefName(value))
8080
throw BadURL("URL '%s' contains an invalid branch/tag name", url);
@@ -403,8 +403,8 @@ struct GitHubInputScheme : GitArchiveInputScheme
403403
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
404404

405405
return RefInfo{
406-
.rev = Hash::parseAny(std::string{json["sha"]}, HashAlgorithm::SHA1),
407-
.treeHash = Hash::parseAny(std::string{json["commit"]["tree"]["sha"]}, HashAlgorithm::SHA1)};
406+
.rev = parseGitHash(std::string{json["sha"]}),
407+
.treeHash = parseGitHash(std::string{json["commit"]["tree"]["sha"]})};
408408
}
409409

410410
DownloadUrl getDownloadUrl(const Input & input) const override
@@ -478,7 +478,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
478478
store->requireStoreObjectAccessor(downloadResult.storePath)->readFile(CanonPath::root));
479479

480480
if (json.is_array() && json.size() >= 1 && json[0]["id"] != nullptr) {
481-
return RefInfo{.rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1)};
481+
return RefInfo{.rev = parseGitHash(std::string(json[0]["id"]))};
482482
}
483483
if (json.is_array() && json.size() == 0) {
484484
throw Error("No commits returned by GitLab API -- does the git ref really exist?");
@@ -579,7 +579,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
579579
if (!id)
580580
throw BadURL("in '%d', couldn't find ref '%d'", input.to_string(), ref);
581581

582-
return RefInfo{.rev = Hash::parseAny(*id, HashAlgorithm::SHA1)};
582+
return RefInfo{.rev = parseGitHash(*id)};
583583
}
584584

585585
DownloadUrl getDownloadUrl(const Input & input) const override

src/libfetchers/include/nix/fetchers/git-utils.hh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,14 @@ struct Setter
167167
*/
168168
bool isLegalRefName(const std::string & refName);
169169

170+
/**
171+
* Parse a base16-encoded git hash string and determine the hash
172+
* algorithm based on the length (40 chars = SHA1, 64 chars = SHA256).
173+
*
174+
* @note For Nix-native information we should *not* do length tricks,
175+
* but instead always rely on an explicit algorithm. This hack should be
176+
* only for foreign hash literals.
177+
*/
178+
Hash parseGitHash(std::string_view hashStr);
179+
170180
} // namespace nix

src/libfetchers/indirect.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct IndirectInputScheme : InputScheme
2323
if (path.size() == 1) {
2424
} else if (path.size() == 2) {
2525
if (std::regex_match(path[1], revRegex))
26-
rev = Hash::parseAny(path[1], HashAlgorithm::SHA1);
26+
rev = parseGitHash(path[1]);
2727
else if (isLegalRefName(path[1]))
2828
ref = path[1];
2929
else
@@ -34,7 +34,7 @@ struct IndirectInputScheme : InputScheme
3434
ref = path[1];
3535
if (!std::regex_match(path[2], revRegex))
3636
throw BadURL("in flake URL '%s', '%s' is not a commit hash", url, path[2]);
37-
rev = Hash::parseAny(path[2], HashAlgorithm::SHA1);
37+
rev = parseGitHash(path[2]);
3838
} else
3939
throw BadURL("GitHub URL '%s' is invalid", url);
4040

0 commit comments

Comments
 (0)