Skip to content

WIP - kick-support#897

Open
Zibbp wants to merge 1 commit intomainfrom
kick-support
Open

WIP - kick-support#897
Zibbp wants to merge 1 commit intomainfrom
kick-support

Conversation

@Zibbp
Copy link
Copy Markdown
Owner

@Zibbp Zibbp commented Oct 1, 2025

Publishing some ground work for Kick I worked on a few months ago. This is not functional at all. Maybe I'll pick this up again in the future.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @Zibbp, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request lays the groundwork for integrating Kick as a supported streaming platform. It extends the application's data models, backend services, and frontend interface to recognize and interact with Kick, enabling future features like archiving Kick live streams and their associated chat. While marked as 'Work In Progress' and not yet fully functional, it establishes the necessary infrastructure for comprehensive Kick support.

Highlights

  • Kick Platform Integration: Introduced foundational support for the Kick streaming platform across the application, enabling future archiving capabilities.
  • Database Schema Update: The platform enum in the database schema (ent/migrate/schema.go) and its validator (ent/vod/vod.go) have been updated to include 'kick' as a supported video platform.
  • Frontend UI Enhancements: The archive page (frontend/app/archive/page.tsx) now includes a platform selection dropdown, allowing users to choose between Twitch and Kick for archiving. The container size was also increased for better layout.
  • Backend Service Extension: The archive service (internal/archive/archive.go) and ytdlp service (internal/exec/ytdlp/ytdlp.go) have been modified to handle Kick-specific logic for retrieving live stream information and processing video details. New environment variables (KICK_CLIENT_ID, KICK_CLIENT_SECRET) have been added for Kick API authentication.
  • Kick-Specific Video and Chat Downloaders: New execution functions (internal/exec/exec_kick.go) have been added to handle downloading live video streams using yt-dlp and live chat via WebSocket for the Kick platform. Dedicated Kick API structures and connection logic (internal/platform/kick.go, internal/platform/kick_api.go, internal/platform/kick_connection.go) are also introduced.
  • Task Worker Updates: The chat, live_chat, and live_video workers (internal/tasks/chat.go, internal/tasks/live_chat.go, internal/tasks/live_video.go) now dynamically select the appropriate platform service based on the video's platform, allowing for Kick-specific download and conversion processes. The PlatformFromContext utility has been updated to support this.
  • Chat Conversion for Kick: A new utility function (internal/utils/tdl.go) has been implemented to convert Kick WebSocket chat data into the TDL (TwitchDownloader) chat format for rendering, including handling of emotes.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

gemini-code-assist[bot]

This comment was marked as resolved.

// ConvertNDJSONToJSONArray converts a newline-delimited JSON file to a JSON array.
func ConvertNDJSONToJSONArrayInPlace(filePath string) error {
// Check if file starts with '['
f, err := os.Open(filePath)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 6 months ago

General fix:
User-controlled file paths must be validated before being used in any filesystem operations, to avoid unauthorized file access or modification. This can be achieved by checking that the user-supplied file path is within a "safe" (whitelisted) directory.

Detailed fix:

  • Clearly define a safe directory (e.g., a temp or uploads folder) in configuration, and ensure all file inputs must reside within that directory.
  • Before using the path, combine it with the safe directory, normalize and resolve it using filepath.Abs/filepath.Clean, and ensure the result actually remains within the safe root.
  • Reject any input not confined to the safe directory.
  • Implement this check at the entry point—most robustly in ConvertKickWebSocketChatToTDLChat—before passing the path along, but you could also defensively check in ConvertNDJSONToJSONArrayInPlace.
  • You will need to import path/filepath and strings (if not already present in the target file).

Specific changes:

  • Edit internal/utils/tdl.go, in the function ConvertKickWebSocketChatToTDLChat (lines 345–352), to validate the path argument before using it. You might add new parameters like a safe root or retrieve it from config.
  • Alternatively, do the validation at the start of ConvertNDJSONToJSONArrayInPlace in internal/utils/chat.go, but to match best practice, validate as early as possible.

Suggested changeset 1
internal/utils/tdl.go
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/internal/utils/tdl.go b/internal/utils/tdl.go
--- a/internal/utils/tdl.go
+++ b/internal/utils/tdl.go
@@ -4,11 +4,12 @@
 	"encoding/json"
 	"fmt"
 	"os"
+	"path/filepath"
 	"regexp"
 	"sort"
 	"strconv"
 	"strings"
-	"time"
+	//"time"
 
 	"github.com/google/uuid"
 	"github.com/rs/zerolog/log"
@@ -350,9 +347,23 @@
 	channelID int,
 	chatStartTime time.Time,
 ) error {
+	// Define a safe directory for chat files (update as appropriate)
+	const SafeChatDir = "/tmp/kickchats" // <-- CHANGE THIS to your actual safe upload dir (e.g., from config)
+	absSafeDir, err := filepath.Abs(SafeChatDir)
+	if err != nil {
+		return fmt.Errorf("failed to resolve safe directory: %w", err)
+	}
+	absPath, err := filepath.Abs(path)
+	if err != nil {
+		return fmt.Errorf("invalid chat file path: %w", err)
+	}
+	if !strings.HasPrefix(absPath, absSafeDir+string(os.PathSeparator)) && absPath != absSafeDir {
+		return fmt.Errorf("chat file path outside allowed directory")
+	}
+
 	log.Debug().Str("chat_file", path).Msg("Converting Kick WebSocket chat to TDL chat for rendering")
 	// Convert to JSON file
-	err := ConvertNDJSONToJSONArrayInPlace(path)
+	err = ConvertNDJSONToJSONArrayInPlace(absPath)
 	if err != nil {
 		return fmt.Errorf("converting kick chat to json: %w", err)
 	}
EOF
@@ -4,11 +4,12 @@
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"time"
//"time"

"github.com/google/uuid"
"github.com/rs/zerolog/log"
@@ -350,9 +347,23 @@
channelID int,
chatStartTime time.Time,
) error {
// Define a safe directory for chat files (update as appropriate)
const SafeChatDir = "/tmp/kickchats" // <-- CHANGE THIS to your actual safe upload dir (e.g., from config)
absSafeDir, err := filepath.Abs(SafeChatDir)
if err != nil {
return fmt.Errorf("failed to resolve safe directory: %w", err)
}
absPath, err := filepath.Abs(path)
if err != nil {
return fmt.Errorf("invalid chat file path: %w", err)
}
if !strings.HasPrefix(absPath, absSafeDir+string(os.PathSeparator)) && absPath != absSafeDir {
return fmt.Errorf("chat file path outside allowed directory")
}

log.Debug().Str("chat_file", path).Msg("Converting Kick WebSocket chat to TDL chat for rendering")
// Convert to JSON file
err := ConvertNDJSONToJSONArrayInPlace(path)
err = ConvertNDJSONToJSONArrayInPlace(absPath)
if err != nil {
return fmt.Errorf("converting kick chat to json: %w", err)
}
Copilot is powered by AI and may make mistakes. Always verify output.

// Temp output file
tmpPath := filePath + ".tmp"
tmp, err := os.Create(tmpPath)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 6 months ago

To safely use user-provided paths in filesystem operations, the code must strictly validate or restrict the user input. The most broadly-applicable method is to ensure that any path provided:

  • Is a filename only (no slashes or ".." segments, if that is the intended use), or
  • If it is a path, it must always be resolved within a safe directory.

In this context, since the input is coming untrusted from the HTTP API and used directly, the correct fix is:

  1. Restrict filePath in ConvertNDJSONToJSONArrayInPlace to ensure it is always within a designated "safe directory" (such as a temp directory or application-data directory).
  2. Accomplish this by resolving the absolute location of the candidate file, and checking its prefix against the safe directory's absolute path. If the check fails, return an error and abort.
  3. This check must be implemented at the entry-point function, before any sensitive file operations are performed.
  4. This will require knowledge of the intended safe directory (this can be injected via parameter, or can be hardcoded/configured as required).

Because of the function signature, the cleanest fix is to add such a check into ConvertNDJSONToJSONArrayInPlace itself, perhaps using an environment variable or config for the safe directory.

Changes required:

  • Add package import(s) for path/filepath and possibly os if not present.
  • Define a safe base directory for all chat file operations. For demonstration, use os.TempDir() or, if available via config, the proper temp/work directory.
  • In ConvertNDJSONToJSONArrayInPlace, resolve and check that filePath (and thus also tmpPath) both reside within the safe directory before further operations.
  • Return an error if the check fails.

No change to functionality is made for legitimate (well-formed) user input, but malicious paths will now be rejected.


Suggested changeset 1
internal/utils/chat.go

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/internal/utils/chat.go b/internal/utils/chat.go
--- a/internal/utils/chat.go
+++ b/internal/utils/chat.go
@@ -5,6 +5,7 @@
 	"fmt"
 	"io"
 	"os"
+	"path/filepath"
 	"regexp"
 	"strings"
 	"unicode"
@@ -45,6 +46,20 @@
 
 // ConvertNDJSONToJSONArray converts a newline-delimited JSON file to a JSON array.
 func ConvertNDJSONToJSONArrayInPlace(filePath string) error {
+	// Validate that filePath is within the allowed temp dir
+	safeDir := os.TempDir() // Ideally, replace with your actual safe directory if needed
+	absSafeDir, err := filepath.Abs(safeDir)
+	if err != nil {
+		return fmt.Errorf("could not resolve safe directory: %w", err)
+	}
+	absInput, err := filepath.Abs(filePath)
+	if err != nil {
+		return fmt.Errorf("could not resolve input file path: %w", err)
+	}
+	if len(absInput) < len(absSafeDir) || absInput[:len(absSafeDir)] != absSafeDir {
+		return fmt.Errorf("file path is outside allowed directory")
+	}
+
 	// Check if file starts with '['
 	f, err := os.Open(filePath)
 	if err != nil {
EOF
@@ -5,6 +5,7 @@
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"unicode"
@@ -45,6 +46,20 @@

// ConvertNDJSONToJSONArray converts a newline-delimited JSON file to a JSON array.
func ConvertNDJSONToJSONArrayInPlace(filePath string) error {
// Validate that filePath is within the allowed temp dir
safeDir := os.TempDir() // Ideally, replace with your actual safe directory if needed
absSafeDir, err := filepath.Abs(safeDir)
if err != nil {
return fmt.Errorf("could not resolve safe directory: %w", err)
}
absInput, err := filepath.Abs(filePath)
if err != nil {
return fmt.Errorf("could not resolve input file path: %w", err)
}
if len(absInput) < len(absSafeDir) || absInput[:len(absSafeDir)] != absSafeDir {
return fmt.Errorf("file path is outside allowed directory")
}

// Check if file starts with '['
f, err := os.Open(filePath)
if err != nil {
Copilot is powered by AI and may make mistakes. Always verify output.
f.Close()

// Replace original file
if err := os.Rename(tmpPath, filePath); err != nil {

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 6 months ago

General approach:
User-controlled paths must never be used directly in filesystem operations without ensuring they cannot be manipulated to access or modify files outside of a safe directory. The best solution is to validate that the provided path is strictly within a designated temporary or upload folder (e.g., envConfig.TempDir), after resolving symlinks and normalizing the input. Reject any input that fails this check.

Detailed fix:

  • In the handler (ConvertKickChat), validate that body.LiveChatPath does not contain any absolute or parent directory navigation, and that the resolved absolute path (when joined with the intended base directory) starts with the safe base directory. If the input fails this check, return an error.
  • Only allow access to files inside a pre-configured directory such as envConfig.TempDir.
  • No changes are needed in downstream functions (ConvertKickWebSocketChatToTDLChat, ConvertNDJSONToJSONArrayInPlace) as long as the input is validated up front.

Files/Regions to change:

  • Edit internal/transport/http/archive.go, in the ConvertKickChat handler, immediately after extracting the body.LiveChatPath value, to validate and restrict it.
  • Add necessary import lines if missing (e.g., path/filepath, strings).

Suggested changeset 1
internal/transport/http/archive.go
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/internal/transport/http/archive.go b/internal/transport/http/archive.go
--- a/internal/transport/http/archive.go
+++ b/internal/transport/http/archive.go
@@ -14,6 +14,8 @@
 	"github.com/zibbp/ganymede/internal/archive"
 	"github.com/zibbp/ganymede/internal/config"
 	"github.com/zibbp/ganymede/internal/utils"
+	"path/filepath"
+	"strings"
 )
 
 type ArchiveService interface {
@@ -232,8 +234,21 @@
 	envConfig := config.GetEnvConfig()
 	outPath := fmt.Sprintf("%s/%s-chat-convert.json", envConfig.TempDir, body.VideoID)
 
-	err = utils.ConvertKickWebSocketChatToTDLChat(body.LiveChatPath, outPath, body.ChannelName, body.VideoID, body.ChannelID, t)
+	// Validate that LiveChatPath is inside TempDir
+	absBase, err := filepath.Abs(envConfig.TempDir)
 	if err != nil {
+		return echo.NewHTTPError(http.StatusInternalServerError, "Invalid server configuration")
+	}
+	absPath, err := filepath.Abs(body.LiveChatPath)
+	if err != nil {
+		return echo.NewHTTPError(http.StatusBadRequest, "Invalid chat path")
+	}
+	if !strings.HasPrefix(absPath, absBase+string(filepath.Separator)) && absPath != absBase {
+		return echo.NewHTTPError(http.StatusBadRequest, "Chat path is outside allowed directory")
+	}
+
+	err = utils.ConvertKickWebSocketChatToTDLChat(absPath, outPath, body.ChannelName, body.VideoID, body.ChannelID, t)
+	if err != nil {
 		return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
 	}
 
EOF
@@ -14,6 +14,8 @@
"github.com/zibbp/ganymede/internal/archive"
"github.com/zibbp/ganymede/internal/config"
"github.com/zibbp/ganymede/internal/utils"
"path/filepath"
"strings"
)

type ArchiveService interface {
@@ -232,8 +234,21 @@
envConfig := config.GetEnvConfig()
outPath := fmt.Sprintf("%s/%s-chat-convert.json", envConfig.TempDir, body.VideoID)

err = utils.ConvertKickWebSocketChatToTDLChat(body.LiveChatPath, outPath, body.ChannelName, body.VideoID, body.ChannelID, t)
// Validate that LiveChatPath is inside TempDir
absBase, err := filepath.Abs(envConfig.TempDir)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Invalid server configuration")
}
absPath, err := filepath.Abs(body.LiveChatPath)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid chat path")
}
if !strings.HasPrefix(absPath, absBase+string(filepath.Separator)) && absPath != absBase {
return echo.NewHTTPError(http.StatusBadRequest, "Chat path is outside allowed directory")
}

err = utils.ConvertKickWebSocketChatToTDLChat(absPath, outPath, body.ChannelName, body.VideoID, body.ChannelID, t)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}

Copilot is powered by AI and may make mistakes. Always verify output.
f.Close()

// Replace original file
if err := os.Rename(tmpPath, filePath); err != nil {

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 6 months ago

To fix this issue, you must ensure that any path constructed from HTTP request data (here, body.LiveChatPath / path in subsequent calls) is validated before being used as a file path. The best way is to ensure the resolved absolute path is contained within an intended safe directory for chat files, using filepath.Abs and checking prefix with the safe directory’s absolute path (which must also be absolute). This check should be placed at the earliest opportunity before the path is passed to file APIs—in this case, within ConvertKickWebSocketChatToTDLChat just before any filesystem operations, since we’re limited to code in internal/utils/tdl.go and internal/utils/chat.go. Additionally, we should propagate the error up so the HTTP handler can report it as a bad request. You will need to:

  • Choose (or determine from context) a safe base directory for chat files, and resolve paths relative to it.
  • Import "path/filepath" and "strings" in internal/utils/tdl.go if needed.
  • Validate that the requested path’s absolute path is within the safe directory.
  • Return an error if validation fails.
Suggested changeset 1
internal/utils/tdl.go
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/internal/utils/tdl.go b/internal/utils/tdl.go
--- a/internal/utils/tdl.go
+++ b/internal/utils/tdl.go
@@ -9,6 +9,7 @@
 	"strconv"
 	"strings"
 	"time"
+	"path/filepath"
 
 	"github.com/google/uuid"
 	"github.com/rs/zerolog/log"
@@ -350,9 +351,23 @@
 	channelID int,
 	chatStartTime time.Time,
 ) error {
+	// Validate that the input path is within the chat storage directory
+	const safeChatDir = "/tmp/kickchats"
+	absSafeDir, err := filepath.Abs(safeChatDir)
+	if err != nil {
+		return fmt.Errorf("unable to resolve safe chat directory: %w", err)
+	}
+	absInputPath, err := filepath.Abs(path)
+	if err != nil {
+		return fmt.Errorf("unable to resolve chat file path: %w", err)
+	}
+	if !strings.HasPrefix(absInputPath, absSafeDir+string(os.PathSeparator)) && absInputPath != absSafeDir {
+		return fmt.Errorf("invalid chat file path: must be within %s", absSafeDir)
+	}
+
 	log.Debug().Str("chat_file", path).Msg("Converting Kick WebSocket chat to TDL chat for rendering")
 	// Convert to JSON file
-	err := ConvertNDJSONToJSONArrayInPlace(path)
+	err = ConvertNDJSONToJSONArrayInPlace(path)
 	if err != nil {
 		return fmt.Errorf("converting kick chat to json: %w", err)
 	}
EOF
@@ -9,6 +9,7 @@
"strconv"
"strings"
"time"
"path/filepath"

"github.com/google/uuid"
"github.com/rs/zerolog/log"
@@ -350,9 +351,23 @@
channelID int,
chatStartTime time.Time,
) error {
// Validate that the input path is within the chat storage directory
const safeChatDir = "/tmp/kickchats"
absSafeDir, err := filepath.Abs(safeChatDir)
if err != nil {
return fmt.Errorf("unable to resolve safe chat directory: %w", err)
}
absInputPath, err := filepath.Abs(path)
if err != nil {
return fmt.Errorf("unable to resolve chat file path: %w", err)
}
if !strings.HasPrefix(absInputPath, absSafeDir+string(os.PathSeparator)) && absInputPath != absSafeDir {
return fmt.Errorf("invalid chat file path: must be within %s", absSafeDir)
}

log.Debug().Str("chat_file", path).Msg("Converting Kick WebSocket chat to TDL chat for rendering")
// Convert to JSON file
err := ConvertNDJSONToJSONArrayInPlace(path)
err = ConvertNDJSONToJSONArrayInPlace(path)
if err != nil {
return fmt.Errorf("converting kick chat to json: %w", err)
}
Copilot is powered by AI and may make mistakes. Always verify output.
}

func OpenKickLiveChatFile(path string) ([]KickWebSocketChatMessage, error) {
liveChatJsonFile, err := os.Open(path)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 6 months ago

To fix the problem, we must restrict the user-supplied path argument so that it cannot escape a "safe directory" intended for temporary chat files (as alluded to in other usages of envConfig.TempDir). The best way is to resolve the path with respect to the intended directory (e.g., the application's temp directory) and ensure the resulting path stays within that directory. This involves joining the provided relative path to the safe base directory, resolving the absolute path, and checking the prefix. If the base directory is not always absolute, resolve it first.

Edit regions:

  • internal/utils/live_chat.go: Edit the OpenKickLiveChatFile function to take a baseDir argument and validate the user input. The path is always constructed by joining this directory with the filename.
  • internal/utils/tdl.go: Update ConvertKickWebSocketChatToTDLChat and any callsites to pass the correct base directory to OpenKickLiveChatFile.
  • Possibly update the HTTP handler to ensure the base directory is given.

Required:

  • Import path/filepath and strings into files where used.
  • Validation code to ensure the path stays within the intended directory.
  • Change usages accordingly.
Suggested changeset 2
internal/utils/live_chat.go

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/internal/utils/live_chat.go b/internal/utils/live_chat.go
--- a/internal/utils/live_chat.go
+++ b/internal/utils/live_chat.go
@@ -6,6 +6,8 @@
 	"io"
 	"os"
 	"time"
+	"path/filepath"
+	"strings"
 
 	"github.com/rs/zerolog/log"
 )
@@ -106,9 +108,23 @@
 	return liveComments, nil
 }
 
-func OpenKickLiveChatFile(path string) ([]KickWebSocketChatMessage, error) {
-	liveChatJsonFile, err := os.Open(path)
+func OpenKickLiveChatFile(baseDir, filename string) ([]KickWebSocketChatMessage, error) {
+	// Validate and clean filename
+	if strings.Contains(filename, "/") || strings.Contains(filename, "\\") || strings.Contains(filename, "..") {
+		return nil, fmt.Errorf("invalid filename")
+	}
+	safeDir, err := filepath.Abs(baseDir)
 	if err != nil {
+		return nil, fmt.Errorf("invalid base directory: %v", err)
+	}
+	fullPath := filepath.Join(safeDir, filename)
+	absPath, err := filepath.Abs(fullPath)
+	if err != nil || !strings.HasPrefix(absPath, safeDir) {
+		return nil, fmt.Errorf("invalid path: %v", err)
+	}
+
+	liveChatJsonFile, err := os.Open(absPath)
+	if err != nil {
 		return nil, fmt.Errorf("failed to open chat file: %v", err)
 	}
 	defer func() {
EOF
@@ -6,6 +6,8 @@
"io"
"os"
"time"
"path/filepath"
"strings"

"github.com/rs/zerolog/log"
)
@@ -106,9 +108,23 @@
return liveComments, nil
}

func OpenKickLiveChatFile(path string) ([]KickWebSocketChatMessage, error) {
liveChatJsonFile, err := os.Open(path)
func OpenKickLiveChatFile(baseDir, filename string) ([]KickWebSocketChatMessage, error) {
// Validate and clean filename
if strings.Contains(filename, "/") || strings.Contains(filename, "\\") || strings.Contains(filename, "..") {
return nil, fmt.Errorf("invalid filename")
}
safeDir, err := filepath.Abs(baseDir)
if err != nil {
return nil, fmt.Errorf("invalid base directory: %v", err)
}
fullPath := filepath.Join(safeDir, filename)
absPath, err := filepath.Abs(fullPath)
if err != nil || !strings.HasPrefix(absPath, safeDir) {
return nil, fmt.Errorf("invalid path: %v", err)
}

liveChatJsonFile, err := os.Open(absPath)
if err != nil {
return nil, fmt.Errorf("failed to open chat file: %v", err)
}
defer func() {
internal/utils/tdl.go
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/internal/utils/tdl.go b/internal/utils/tdl.go
--- a/internal/utils/tdl.go
+++ b/internal/utils/tdl.go
@@ -357,7 +357,10 @@
 		return fmt.Errorf("converting kick chat to json: %w", err)
 	}
 
-	liveComments, err := OpenKickLiveChatFile(path)
+	// Use a safe directory, e.g. from config/environment
+	envConfig := config.GetEnvConfig()
+	filename := filepath.Base(path)
+	liveComments, err := OpenKickLiveChatFile(envConfig.TempDir, filename)
 	if err != nil {
 		return fmt.Errorf("failed to open kick chat file: %v", err)
 	}
EOF
@@ -357,7 +357,10 @@
return fmt.Errorf("converting kick chat to json: %w", err)
}

liveComments, err := OpenKickLiveChatFile(path)
// Use a safe directory, e.g. from config/environment
envConfig := config.GetEnvConfig()
filename := filepath.Base(path)
liveComments, err := OpenKickLiveChatFile(envConfig.TempDir, filename)
if err != nil {
return fmt.Errorf("failed to open kick chat file: %v", err)
}
Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants