Ctrl K
ComunidadGuías…

cli/support_internal_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package cli

import (
	"archive/tar"
	"archive/zip"
	"bytes"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/coder/coder/v2/testutil"
)

func TestSafeWorkspaceFilesArchiveEntryName(t *testing.T) {
	t.Parallel()

	for _, tt := range []struct {
		name string
		ok   bool
	}{
		{name: "manifest.json", ok: true},
		{name: "files/server.log", ok: true},
		{name: "./files/server.log", ok: false},
		{name: "../manifest.json", ok: false},
		{name: "/manifest.json", ok: false},
		{name: "files/nested/../server.log", ok: false},
		{name: "files/../../manifest.json", ok: false},
		{name: "files\\nested\\server.log", ok: false},
		{name: `files/nested\..\server.log`, ok: false},
		{name: "other/server.log", ok: false},
	} {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			got, ok := safeWorkspaceFilesArchiveEntryName(tt.name)
			require.Equal(t, tt.ok, ok)
			if tt.ok {
				require.Equal(t, tt.name, got)
			}
		})
	}
}

func TestWriteWorkspaceFilesArchive(t *testing.T) {
	t.Parallel()

	t.Run("UnpacksManifestAndFiles", func(t *testing.T) {
		t.Parallel()

		agentArchive := makeWorkspaceFilesArchive(t,
			"files/server.log", "server log",
			"manifest.json", `{"files":[{"archive_path":"files/server.log"}]}`,
			"../escape.log", "should be dropped and recorded",
		)

		var bundle bytes.Buffer
		bundleZip := zip.NewWriter(&bundle)
		require.NoError(t, writeWorkspaceFilesArchive(agentArchive, bundleZip, supportBundleWorkspaceFilesMaxBytes))
		require.NoError(t, bundleZip.Close())

		entries := testutil.ReadZip(t, bundle.Bytes())
		require.Equal(t, "server log", string(entries["agent/workspace_files/files/server.log"]))
		require.Contains(t, entries, "agent/workspace_files/manifest.json")
		require.Contains(t, string(entries["agent/workspace_files/collection_errors.txt"]), "../escape.log")
		require.Len(t, entries, 3)
	})

	t.Run("AbortsOnEntryBeyondBudget", func(t *testing.T) {
		t.Parallel()

		agentArchive := makeWorkspaceFilesArchive(t,
			"files/ok.log", "ok",
			"files/big.log", "this entry is too big",
			"files/after.log", "never reached",
		)

		var bundle bytes.Buffer
		bundleZip := zip.NewWriter(&bundle)
		// A 4 byte budget fits ok.log; big.log exceeds it and aborts the
		// rest.
		require.NoError(t, writeWorkspaceFilesArchive(agentArchive, bundleZip, 4))
		require.NoError(t, bundleZip.Close())

		entries := testutil.ReadZip(t, bundle.Bytes())
		require.Equal(t, "ok", string(entries["agent/workspace_files/files/ok.log"]))
		require.NotContains(t, entries, "agent/workspace_files/files/big.log")
		require.NotContains(t, entries, "agent/workspace_files/files/after.log")
		errs := string(entries["agent/workspace_files/collection_errors.txt"])
		require.Contains(t, errs, "files/big.log")
		require.Contains(t, errs, "budget")
	})

	t.Run("MalformedArchiveDoesNotFail", func(t *testing.T) {
		t.Parallel()

		var bundle bytes.Buffer
		bundleZip := zip.NewWriter(&bundle)
		require.NoError(t, writeWorkspaceFilesArchive([]byte("not a tar"), bundleZip, supportBundleWorkspaceFilesMaxBytes))
		require.NoError(t, bundleZip.Close())

		entries := testutil.ReadZip(t, bundle.Bytes())
		require.Contains(t, string(entries["agent/workspace_files/collection_errors.txt"]), "read workspace files archive")
	})
}

// makeWorkspaceFilesArchive tars alternating name/content pairs in order.
func makeWorkspaceFilesArchive(t *testing.T, pairs ...string) []byte {
	t.Helper()

	require.Zero(t, len(pairs)%2)
	var buf bytes.Buffer
	tw := tar.NewWriter(&buf)
	for i := 0; i < len(pairs); i += 2 {
		require.NoError(t, tw.WriteHeader(&tar.Header{
			Name: pairs[i],
			Mode: 0o644,
			Size: int64(len(pairs[i+1])),
		}))
		_, err := tw.Write([]byte(pairs[i+1]))
		require.NoError(t, err)
	}
	require.NoError(t, tw.Close())
	return buf.Bytes()
}