isomorphic-git 1.38.7 → 1.38.9

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.
@@ -5868,6 +5868,31 @@ async function _writeTree({ fs, gitdir, tree }) {
5868
5868
  return oid
5869
5869
  }
5870
5870
 
5871
+ /**
5872
+ * Refuse to materialize a working-tree entry whose parent path traverses a
5873
+ * symbolic link. A leading component that is a symlink could otherwise redirect
5874
+ * a write or mkdir outside the working tree. git applies the same check: it does
5875
+ * not follow symlinks in the leading path when writing working-tree files.
5876
+ *
5877
+ * @param {import('../models/FileSystem.js').FileSystem} fs
5878
+ * @param {string} dir
5879
+ * @param {string} fullpath
5880
+ */
5881
+ async function assertNoSymlinkInLeadingPath(fs, dir, fullpath) {
5882
+ const parts = fullpath.split('/');
5883
+ parts.pop(); // the final segment is the entry being written, not a leading dir
5884
+ let current = dir;
5885
+ for (const part of parts) {
5886
+ if (part === '' || part === '.') continue
5887
+ current = `${current}/${part}`;
5888
+ const stats = await fs.lstat(current);
5889
+ // lstat returns null when the path doesn't exist yet (nothing to traverse).
5890
+ if (stats && stats.isSymbolicLink()) {
5891
+ throw new UnsafeFilepathError(fullpath)
5892
+ }
5893
+ }
5894
+ }
5895
+
5871
5896
  function posixifyPathBuffer(buffer) {
5872
5897
  let idx;
5873
5898
  while (~(idx = buffer.indexOf(92))) buffer[idx] = 47;
@@ -6124,6 +6149,11 @@ async function applyTreeChanges({
6124
6149
  await fs.rmdir(currentFilepath);
6125
6150
  break
6126
6151
  case 'mkdir':
6152
+ // Don't mkdir through a symlinked leading path: a parent component that
6153
+ // is a symlink could otherwise redirect the directory creation outside
6154
+ // the working tree. git applies the same check (it does not follow
6155
+ // symlinks here) — see checkout.
6156
+ await assertNoSymlinkInLeadingPath(fs, dir, op.filepath);
6127
6157
  await fs.mkdir(currentFilepath);
6128
6158
  break
6129
6159
  case 'rm':
@@ -6136,6 +6166,10 @@ async function applyTreeChanges({
6136
6166
  currentFilepath.startsWith(removedDir)
6137
6167
  )
6138
6168
  ) {
6169
+ // Don't write through a symlinked leading path (see checkout): a
6170
+ // parent component that is a symlink could redirect the write to a
6171
+ // location outside the working tree.
6172
+ await assertNoSymlinkInLeadingPath(fs, dir, op.filepath);
6139
6173
  const { object } = await _readObject({
6140
6174
  fs,
6141
6175
  cache: {},
package/managers/index.js CHANGED
@@ -5855,6 +5855,31 @@ async function _writeTree({ fs, gitdir, tree }) {
5855
5855
  return oid
5856
5856
  }
5857
5857
 
5858
+ /**
5859
+ * Refuse to materialize a working-tree entry whose parent path traverses a
5860
+ * symbolic link. A leading component that is a symlink could otherwise redirect
5861
+ * a write or mkdir outside the working tree. git applies the same check: it does
5862
+ * not follow symlinks in the leading path when writing working-tree files.
5863
+ *
5864
+ * @param {import('../models/FileSystem.js').FileSystem} fs
5865
+ * @param {string} dir
5866
+ * @param {string} fullpath
5867
+ */
5868
+ async function assertNoSymlinkInLeadingPath(fs, dir, fullpath) {
5869
+ const parts = fullpath.split('/');
5870
+ parts.pop(); // the final segment is the entry being written, not a leading dir
5871
+ let current = dir;
5872
+ for (const part of parts) {
5873
+ if (part === '' || part === '.') continue
5874
+ current = `${current}/${part}`;
5875
+ const stats = await fs.lstat(current);
5876
+ // lstat returns null when the path doesn't exist yet (nothing to traverse).
5877
+ if (stats && stats.isSymbolicLink()) {
5878
+ throw new UnsafeFilepathError(fullpath)
5879
+ }
5880
+ }
5881
+ }
5882
+
5858
5883
  function posixifyPathBuffer(buffer) {
5859
5884
  let idx;
5860
5885
  while (~(idx = buffer.indexOf(92))) buffer[idx] = 47;
@@ -6111,6 +6136,11 @@ async function applyTreeChanges({
6111
6136
  await fs.rmdir(currentFilepath);
6112
6137
  break
6113
6138
  case 'mkdir':
6139
+ // Don't mkdir through a symlinked leading path: a parent component that
6140
+ // is a symlink could otherwise redirect the directory creation outside
6141
+ // the working tree. git applies the same check (it does not follow
6142
+ // symlinks here) — see checkout.
6143
+ await assertNoSymlinkInLeadingPath(fs, dir, op.filepath);
6114
6144
  await fs.mkdir(currentFilepath);
6115
6145
  break
6116
6146
  case 'rm':
@@ -6123,6 +6153,10 @@ async function applyTreeChanges({
6123
6153
  currentFilepath.startsWith(removedDir)
6124
6154
  )
6125
6155
  ) {
6156
+ // Don't write through a symlinked leading path (see checkout): a
6157
+ // parent component that is a symlink could redirect the write to a
6158
+ // location outside the working tree.
6159
+ await assertNoSymlinkInLeadingPath(fs, dir, op.filepath);
6126
6160
  const { object } = await _readObject({
6127
6161
  fs,
6128
6162
  cache: {},