isomorphic-git 1.42.1 → 1.42.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs CHANGED
@@ -6,12 +6,12 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau
6
6
 
7
7
  var Hash = _interopDefault(require('sha.js/sha1.js'));
8
8
  var AsyncLock = _interopDefault(require('async-lock'));
9
+ var cleanGitRef = _interopDefault(require('clean-git-ref'));
9
10
  var crc32 = _interopDefault(require('crc-32'));
10
11
  var pako = _interopDefault(require('pako'));
11
12
  var crypto$1 = require('crypto');
12
13
  var pify = _interopDefault(require('pify'));
13
14
  var ignore = _interopDefault(require('ignore'));
14
- var cleanGitRef = _interopDefault(require('clean-git-ref'));
15
15
  var diff3Merge = _interopDefault(require('diff3'));
16
16
 
17
17
  /**
@@ -1558,6 +1558,22 @@ function compareRefNames(a, b) {
1558
1558
  return tmp
1559
1559
  }
1560
1560
 
1561
+ /*
1562
+ Adapted from is-git-ref-name-valid
1563
+ SPDX-License-Identifier: MIT
1564
+ Copyright © Vincent Weevers
1565
+ */
1566
+
1567
+ // eslint-disable-next-line no-control-regex
1568
+ const bad = /(^|[/.])([/.]|$)|^@$|@{|[\x00-\x20\x7f~^:?*[\\]|\.lock(\/|$)/;
1569
+
1570
+ function isValidRef(name, onelevel) {
1571
+ if (typeof name !== 'string')
1572
+ throw new TypeError('Reference name must be a string')
1573
+
1574
+ return !bad.test(name) && (!!onelevel || name.includes('/'))
1575
+ }
1576
+
1561
1577
  /*!
1562
1578
  * This code for `path.join` is directly copied from @zenfs/core/path for bundle size improvements.
1563
1579
  * SPDX-License-Identifier: LGPL-3.0-or-later
@@ -2061,6 +2077,13 @@ function assertWritableRef(ref) {
2061
2077
  if (GIT_FILES.includes(ref)) {
2062
2078
  throw new InvalidRefNameError(ref, `refs/heads/${ref}`)
2063
2079
  }
2080
+ // A ref name is joined onto gitdir with `join()`, which collapses `..`
2081
+ // exactly like `path.join`. A server-supplied ref/tag name or the wildcard
2082
+ // capture of a refspec can therefore climb out of gitdir, or anywhere
2083
+ // beneath it, entirely unvalidated up to this point. See GHSA-h3c3-jh3g-8hcc.
2084
+ if (!isValidRef(ref, true)) {
2085
+ throw new InvalidRefNameError(ref, cleanGitRef.clean(ref))
2086
+ }
2064
2087
  }
2065
2088
 
2066
2089
  /**
@@ -2120,21 +2143,39 @@ class GitRefManager {
2120
2143
  ])) {
2121
2144
  const symtarget = refspec.translateOne(symrefs.get(serverRef));
2122
2145
  if (symtarget) {
2146
+ // The destination (translatedRef) is validated below, but the target
2147
+ // a symref points at is server-supplied too (the wire parser accepts
2148
+ // `symref=HEAD:(.*)` unrestricted) and only ever passed through the
2149
+ // refspec's plain string substitution, never checked. Left alone, a
2150
+ // `..`-laden target is written into the file content as-is and later
2151
+ // drives an out-of-gitdir read the next time something resolves it.
2152
+ // See GHSA-h3c3-jh3g-8hcc.
2153
+ assertWritableRef(symtarget);
2123
2154
  symrefTranslations.push([translatedRef, `ref: ${symtarget}`]);
2124
2155
  }
2125
2156
  }
2157
+ // Tags aren't translated by a refspec, but they are still a server-supplied
2158
+ // name written straight to gitdir below, so they need the same check.
2159
+ // Computed here (pure, no I/O) so it can be validated up front and then
2160
+ // reused unchanged in the write step further down.
2161
+ const tagRefsToWrite = tags
2162
+ ? [...refs.keys()].filter(
2163
+ serverRef =>
2164
+ serverRef.startsWith('refs/tags') && !serverRef.endsWith('^{}')
2165
+ )
2166
+ : [];
2126
2167
  // The local side of a refspec is whatever `remote.<name>.fetch` says, so a
2127
2168
  // config like `+refs/heads/main:index` lands a write on `.git/index`.
2128
2169
  // Refuse it here rather than at the write loop: `pruneTags` and `prune`
2129
2170
  // delete refs in between, so a later throw leaves the repository pruned
2130
- // and not updated. The tags added below are always `refs/tags/...`, which
2131
- // is never a system file, so nothing is missed by checking this early.
2171
+ // and not updated.
2132
2172
  for (const [, translatedRef] of refTranslations) {
2133
2173
  assertWritableRef(translatedRef);
2134
2174
  }
2135
2175
  for (const [translatedRef] of symrefTranslations) {
2136
2176
  assertWritableRef(translatedRef);
2137
2177
  }
2178
+ tagRefsToWrite.forEach(assertWritableRef);
2138
2179
  // Delete all current tags if the pruneTags argument is true.
2139
2180
  if (pruneTags) {
2140
2181
  const tags = await GitRefManager.listRefs({
@@ -2149,16 +2190,12 @@ class GitRefManager {
2149
2190
  });
2150
2191
  }
2151
2192
  // Add all tags if the fetch tags argument is true.
2152
- if (tags) {
2153
- for (const serverRef of refs.keys()) {
2154
- if (serverRef.startsWith('refs/tags') && !serverRef.endsWith('^{}')) {
2155
- // Git's behavior is to only fetch tags that do not conflict with tags already present.
2156
- if (!(await GitRefManager.exists({ fs, gitdir, ref: serverRef }))) {
2157
- // Always use the object id of the tag itself, and not the peeled object id.
2158
- const oid = refs.get(serverRef);
2159
- actualRefsToWrite.set(serverRef, oid);
2160
- }
2161
- }
2193
+ for (const serverRef of tagRefsToWrite) {
2194
+ // Git's behavior is to only fetch tags that do not conflict with tags already present.
2195
+ if (!(await GitRefManager.exists({ fs, gitdir, ref: serverRef }))) {
2196
+ // Always use the object id of the tag itself, and not the peeled object id.
2197
+ const oid = refs.get(serverRef);
2198
+ actualRefsToWrite.set(serverRef, oid);
2162
2199
  }
2163
2200
  }
2164
2201
  // Combine refs and symrefs giving symrefs priority
@@ -6219,12 +6256,15 @@ async function _commit({
6219
6256
  gitdir,
6220
6257
  ref,
6221
6258
  });
6222
- refCommit = await _readCommit({ fs, gitdir, oid: refOid, cache: {} });
6223
6259
  } catch {
6224
6260
  // We assume that there's no commit and this is the initial commit
6225
6261
  initialCommit = true;
6226
6262
  }
6227
6263
 
6264
+ if (!initialCommit && (amend || disallowEmpty)) {
6265
+ refCommit = await _readCommit({ fs, gitdir, oid: refOid, cache });
6266
+ }
6267
+
6228
6268
  if (amend && initialCommit) {
6229
6269
  throw new NoCommitError(ref)
6230
6270
  }
@@ -6698,22 +6738,6 @@ async function addNote({
6698
6738
  }
6699
6739
  }
6700
6740
 
6701
- /*
6702
- Adapted from is-git-ref-name-valid
6703
- SPDX-License-Identifier: MIT
6704
- Copyright © Vincent Weevers
6705
- */
6706
-
6707
- // eslint-disable-next-line no-control-regex
6708
- const bad = /(^|[/.])([/.]|$)|^@$|@{|[\x00-\x20\x7f~^:?*[\\]|\.lock(\/|$)/;
6709
-
6710
- function isValidRef(name, onelevel) {
6711
- if (typeof name !== 'string')
6712
- throw new TypeError('Reference name must be a string')
6713
-
6714
- return !bad.test(name) && (!!onelevel || name.includes('/'))
6715
- }
6716
-
6717
6741
  // @ts-check
6718
6742
 
6719
6743
  /**
@@ -9779,8 +9803,8 @@ function filterCapabilities(server, client) {
9779
9803
 
9780
9804
  const pkg = {
9781
9805
  name: 'isomorphic-git',
9782
- version: '1.42.1',
9783
- agent: 'git/isomorphic-git@1.42.1',
9806
+ version: '1.42.3',
9807
+ agent: 'git/isomorphic-git@1.42.3',
9784
9808
  };
9785
9809
 
9786
9810
  class FIFO {
@@ -14615,7 +14639,7 @@ async function _push({
14615
14639
  ) {
14616
14640
  // TODO: I think this should actually be using a refspec transform rather than assuming 'refs/remotes/{remote}'
14617
14641
  const ref = `refs/remotes/${remote}/${fullRemoteRef.replace(
14618
- 'refs/heads',
14642
+ 'refs/heads/',
14619
14643
  ''
14620
14644
  )}`;
14621
14645
  if (_delete) {
package/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import Hash from 'sha.js/sha1.js';
2
2
  import AsyncLock from 'async-lock';
3
+ import cleanGitRef from 'clean-git-ref';
3
4
  import crc32 from 'crc-32';
4
5
  import pako from 'pako';
5
6
  import pify from 'pify';
6
7
  import ignore from 'ignore';
7
- import cleanGitRef from 'clean-git-ref';
8
8
  import diff3Merge from 'diff3';
9
9
 
10
10
  /**
@@ -1551,6 +1551,22 @@ function compareRefNames(a, b) {
1551
1551
  return tmp
1552
1552
  }
1553
1553
 
1554
+ /*
1555
+ Adapted from is-git-ref-name-valid
1556
+ SPDX-License-Identifier: MIT
1557
+ Copyright © Vincent Weevers
1558
+ */
1559
+
1560
+ // eslint-disable-next-line no-control-regex
1561
+ const bad = /(^|[/.])([/.]|$)|^@$|@{|[\x00-\x20\x7f~^:?*[\\]|\.lock(\/|$)/;
1562
+
1563
+ function isValidRef(name, onelevel) {
1564
+ if (typeof name !== 'string')
1565
+ throw new TypeError('Reference name must be a string')
1566
+
1567
+ return !bad.test(name) && (!!onelevel || name.includes('/'))
1568
+ }
1569
+
1554
1570
  /*!
1555
1571
  * This code for `path.join` is directly copied from @zenfs/core/path for bundle size improvements.
1556
1572
  * SPDX-License-Identifier: LGPL-3.0-or-later
@@ -2054,6 +2070,13 @@ function assertWritableRef(ref) {
2054
2070
  if (GIT_FILES.includes(ref)) {
2055
2071
  throw new InvalidRefNameError(ref, `refs/heads/${ref}`)
2056
2072
  }
2073
+ // A ref name is joined onto gitdir with `join()`, which collapses `..`
2074
+ // exactly like `path.join`. A server-supplied ref/tag name or the wildcard
2075
+ // capture of a refspec can therefore climb out of gitdir, or anywhere
2076
+ // beneath it, entirely unvalidated up to this point. See GHSA-h3c3-jh3g-8hcc.
2077
+ if (!isValidRef(ref, true)) {
2078
+ throw new InvalidRefNameError(ref, cleanGitRef.clean(ref))
2079
+ }
2057
2080
  }
2058
2081
 
2059
2082
  /**
@@ -2113,21 +2136,39 @@ class GitRefManager {
2113
2136
  ])) {
2114
2137
  const symtarget = refspec.translateOne(symrefs.get(serverRef));
2115
2138
  if (symtarget) {
2139
+ // The destination (translatedRef) is validated below, but the target
2140
+ // a symref points at is server-supplied too (the wire parser accepts
2141
+ // `symref=HEAD:(.*)` unrestricted) and only ever passed through the
2142
+ // refspec's plain string substitution, never checked. Left alone, a
2143
+ // `..`-laden target is written into the file content as-is and later
2144
+ // drives an out-of-gitdir read the next time something resolves it.
2145
+ // See GHSA-h3c3-jh3g-8hcc.
2146
+ assertWritableRef(symtarget);
2116
2147
  symrefTranslations.push([translatedRef, `ref: ${symtarget}`]);
2117
2148
  }
2118
2149
  }
2150
+ // Tags aren't translated by a refspec, but they are still a server-supplied
2151
+ // name written straight to gitdir below, so they need the same check.
2152
+ // Computed here (pure, no I/O) so it can be validated up front and then
2153
+ // reused unchanged in the write step further down.
2154
+ const tagRefsToWrite = tags
2155
+ ? [...refs.keys()].filter(
2156
+ serverRef =>
2157
+ serverRef.startsWith('refs/tags') && !serverRef.endsWith('^{}')
2158
+ )
2159
+ : [];
2119
2160
  // The local side of a refspec is whatever `remote.<name>.fetch` says, so a
2120
2161
  // config like `+refs/heads/main:index` lands a write on `.git/index`.
2121
2162
  // Refuse it here rather than at the write loop: `pruneTags` and `prune`
2122
2163
  // delete refs in between, so a later throw leaves the repository pruned
2123
- // and not updated. The tags added below are always `refs/tags/...`, which
2124
- // is never a system file, so nothing is missed by checking this early.
2164
+ // and not updated.
2125
2165
  for (const [, translatedRef] of refTranslations) {
2126
2166
  assertWritableRef(translatedRef);
2127
2167
  }
2128
2168
  for (const [translatedRef] of symrefTranslations) {
2129
2169
  assertWritableRef(translatedRef);
2130
2170
  }
2171
+ tagRefsToWrite.forEach(assertWritableRef);
2131
2172
  // Delete all current tags if the pruneTags argument is true.
2132
2173
  if (pruneTags) {
2133
2174
  const tags = await GitRefManager.listRefs({
@@ -2142,16 +2183,12 @@ class GitRefManager {
2142
2183
  });
2143
2184
  }
2144
2185
  // Add all tags if the fetch tags argument is true.
2145
- if (tags) {
2146
- for (const serverRef of refs.keys()) {
2147
- if (serverRef.startsWith('refs/tags') && !serverRef.endsWith('^{}')) {
2148
- // Git's behavior is to only fetch tags that do not conflict with tags already present.
2149
- if (!(await GitRefManager.exists({ fs, gitdir, ref: serverRef }))) {
2150
- // Always use the object id of the tag itself, and not the peeled object id.
2151
- const oid = refs.get(serverRef);
2152
- actualRefsToWrite.set(serverRef, oid);
2153
- }
2154
- }
2186
+ for (const serverRef of tagRefsToWrite) {
2187
+ // Git's behavior is to only fetch tags that do not conflict with tags already present.
2188
+ if (!(await GitRefManager.exists({ fs, gitdir, ref: serverRef }))) {
2189
+ // Always use the object id of the tag itself, and not the peeled object id.
2190
+ const oid = refs.get(serverRef);
2191
+ actualRefsToWrite.set(serverRef, oid);
2155
2192
  }
2156
2193
  }
2157
2194
  // Combine refs and symrefs giving symrefs priority
@@ -6206,12 +6243,15 @@ async function _commit({
6206
6243
  gitdir,
6207
6244
  ref,
6208
6245
  });
6209
- refCommit = await _readCommit({ fs, gitdir, oid: refOid, cache: {} });
6210
6246
  } catch {
6211
6247
  // We assume that there's no commit and this is the initial commit
6212
6248
  initialCommit = true;
6213
6249
  }
6214
6250
 
6251
+ if (!initialCommit && (amend || disallowEmpty)) {
6252
+ refCommit = await _readCommit({ fs, gitdir, oid: refOid, cache });
6253
+ }
6254
+
6215
6255
  if (amend && initialCommit) {
6216
6256
  throw new NoCommitError(ref)
6217
6257
  }
@@ -6685,22 +6725,6 @@ async function addNote({
6685
6725
  }
6686
6726
  }
6687
6727
 
6688
- /*
6689
- Adapted from is-git-ref-name-valid
6690
- SPDX-License-Identifier: MIT
6691
- Copyright © Vincent Weevers
6692
- */
6693
-
6694
- // eslint-disable-next-line no-control-regex
6695
- const bad = /(^|[/.])([/.]|$)|^@$|@{|[\x00-\x20\x7f~^:?*[\\]|\.lock(\/|$)/;
6696
-
6697
- function isValidRef(name, onelevel) {
6698
- if (typeof name !== 'string')
6699
- throw new TypeError('Reference name must be a string')
6700
-
6701
- return !bad.test(name) && (!!onelevel || name.includes('/'))
6702
- }
6703
-
6704
6728
  // @ts-check
6705
6729
 
6706
6730
  /**
@@ -9766,8 +9790,8 @@ function filterCapabilities(server, client) {
9766
9790
 
9767
9791
  const pkg = {
9768
9792
  name: 'isomorphic-git',
9769
- version: '1.42.1',
9770
- agent: 'git/isomorphic-git@1.42.1',
9793
+ version: '1.42.3',
9794
+ agent: 'git/isomorphic-git@1.42.3',
9771
9795
  };
9772
9796
 
9773
9797
  class FIFO {
@@ -14602,7 +14626,7 @@ async function _push({
14602
14626
  ) {
14603
14627
  // TODO: I think this should actually be using a refspec transform rather than assuming 'refs/remotes/{remote}'
14604
14628
  const ref = `refs/remotes/${remote}/${fullRemoteRef.replace(
14605
- 'refs/heads',
14629
+ 'refs/heads/',
14606
14630
  ''
14607
14631
  )}`;
14608
14632
  if (_delete) {