Skip to content

Smart contract development editor with AI #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

dviejokfs
Copy link
Contributor

  • Added the github.com/sashabaranov/go-openai dependency to go.mod for integrating OpenAI functionalities.
  • Updated sqlc.yaml to include an additional SQL queries file for development queries, improving database operations.
  • Introduced new SQL queries in dev-queries.sql for managing projects, including listing, creating, and deleting projects.
  • Enhanced the database model in models.go to include new structures for conversations and tool calls, supporting additional functionalities.
  • Refactored the querier.go interface to include new methods for managing conversations and tool calls, ensuring better separation of concerns.

These changes improve the overall functionality and maintainability of the application, providing better tools for project management and database interactions.

- Added the `github.com/sashabaranov/go-openai` dependency to `go.mod` for integrating OpenAI functionalities.
- Updated `sqlc.yaml` to include an additional SQL queries file for development queries, improving database operations.
- Introduced new SQL queries in `dev-queries.sql` for managing projects, including listing, creating, and deleting projects.
- Enhanced the database model in `models.go` to include new structures for conversations and tool calls, supporting additional functionalities.
- Refactored the `querier.go` interface to include new methods for managing conversations and tool calls, ensuring better separation of concerns.

These changes improve the overall functionality and maintainability of the application, providing better tools for project management and database interactions.

Signed-off-by: David VIEJO <dviejo@kungfusoftware.es>
return errors.New("dir is required")
}
base := filepath.Join(project, dir)
return os.MkdirAll(base, 0755)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 2 days ago

To fix the issue, the dir parameter must be validated before constructing the file path. Validation should ensure that:

  1. The dir parameter does not contain path traversal sequences (..) or path separators (/ or \).
  2. The resolved path remains within the intended directory scope.

The best approach is to:

  • Normalize the dir parameter using filepath.Clean.
  • Check for invalid characters or sequences (.., /, \) in the cleaned path.
  • Ensure the resolved path is within the project directory using filepath.Abs and strings.HasPrefix.

Changes are required in the CreateDir method in pkg/scai/dirs/dirs.go.


Suggested changeset 1
pkg/scai/dirs/dirs.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/pkg/scai/dirs/dirs.go b/pkg/scai/dirs/dirs.go
--- a/pkg/scai/dirs/dirs.go
+++ b/pkg/scai/dirs/dirs.go
@@ -55,4 +55,14 @@
 	}
-	base := filepath.Join(project, dir)
-	return os.MkdirAll(base, 0755)
+	// Normalize and validate the directory name
+	cleanDir := filepath.Clean(dir)
+	if strings.Contains(cleanDir, "..") || strings.Contains(cleanDir, "/") || strings.Contains(cleanDir, "\\") {
+		return errors.New("invalid directory name")
+	}
+	// Ensure the resolved path is within the project directory
+	base := filepath.Join(project, cleanDir)
+	absBase, err := filepath.Abs(base)
+	if err != nil || !strings.HasPrefix(absBase, filepath.Clean(project)) {
+		return errors.New("directory path is outside the project scope")
+	}
+	return os.MkdirAll(absBase, 0755)
 }
EOF
@@ -55,4 +55,14 @@
}
base := filepath.Join(project, dir)
return os.MkdirAll(base, 0755)
// Normalize and validate the directory name
cleanDir := filepath.Clean(dir)
if strings.Contains(cleanDir, "..") || strings.Contains(cleanDir, "/") || strings.Contains(cleanDir, "\\") {
return errors.New("invalid directory name")
}
// Ensure the resolved path is within the project directory
base := filepath.Join(project, cleanDir)
absBase, err := filepath.Abs(base)
if err != nil || !strings.HasPrefix(absBase, filepath.Clean(project)) {
return errors.New("directory path is outside the project scope")
}
return os.MkdirAll(absBase, 0755)
}
Copilot is powered by AI and may make mistakes. Always verify output.
return errors.New("dir is required")
}
base := filepath.Join(project, dir)
return os.RemoveAll(base)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 2 days ago

To fix the issue, the dir parameter must be validated before constructing file paths. Validation should ensure that:

  1. The dir parameter does not contain path traversal sequences (..) or path separators (/ or \).
  2. The resolved path remains within the intended project directory.

The best approach is to:

  1. Normalize the path using filepath.Abs and ensure it is within the project directory.
  2. Reject any input containing invalid characters or sequences.

Changes are required in the DeleteDir, ListDirs, CreateDir, and ListEntries methods in pkg/scai/dirs/dirs.go.


Suggested changeset 1
pkg/scai/dirs/dirs.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/pkg/scai/dirs/dirs.go b/pkg/scai/dirs/dirs.go
--- a/pkg/scai/dirs/dirs.go
+++ b/pkg/scai/dirs/dirs.go
@@ -34,3 +34,6 @@
 	// Scope to project root
-	base := filepath.Join(project, dir)
+	base, err := filepath.Abs(filepath.Join(project, dir))
+	if err != nil || !strings.HasPrefix(base, filepath.Clean(project)+string(os.PathSeparator)) {
+		return nil, errors.New("invalid directory path")
+	}
 	entries, err := os.ReadDir(base)
@@ -55,3 +58,6 @@
 	}
-	base := filepath.Join(project, dir)
+	base, err := filepath.Abs(filepath.Join(project, dir))
+	if err != nil || !strings.HasPrefix(base, filepath.Clean(project)+string(os.PathSeparator)) {
+		return errors.New("invalid directory path")
+	}
 	return os.MkdirAll(base, 0755)
@@ -66,3 +72,6 @@
 	}
-	base := filepath.Join(project, dir)
+	base, err := filepath.Abs(filepath.Join(project, dir))
+	if err != nil || !strings.HasPrefix(base, filepath.Clean(project)+string(os.PathSeparator)) {
+		return errors.New("invalid directory path")
+	}
 	return os.RemoveAll(base)
@@ -78,3 +87,6 @@
 	}
-	base := filepath.Join(project, dir)
+	base, err := filepath.Abs(filepath.Join(project, dir))
+	if err != nil || !strings.HasPrefix(base, filepath.Clean(project)+string(os.PathSeparator)) {
+		return nil, nil, nil, errors.New("invalid directory path")
+	}
 	entries, err := os.ReadDir(base)
EOF
@@ -34,3 +34,6 @@
// Scope to project root
base := filepath.Join(project, dir)
base, err := filepath.Abs(filepath.Join(project, dir))
if err != nil || !strings.HasPrefix(base, filepath.Clean(project)+string(os.PathSeparator)) {
return nil, errors.New("invalid directory path")
}
entries, err := os.ReadDir(base)
@@ -55,3 +58,6 @@
}
base := filepath.Join(project, dir)
base, err := filepath.Abs(filepath.Join(project, dir))
if err != nil || !strings.HasPrefix(base, filepath.Clean(project)+string(os.PathSeparator)) {
return errors.New("invalid directory path")
}
return os.MkdirAll(base, 0755)
@@ -66,3 +72,6 @@
}
base := filepath.Join(project, dir)
base, err := filepath.Abs(filepath.Join(project, dir))
if err != nil || !strings.HasPrefix(base, filepath.Clean(project)+string(os.PathSeparator)) {
return errors.New("invalid directory path")
}
return os.RemoveAll(base)
@@ -78,3 +87,6 @@
}
base := filepath.Join(project, dir)
base, err := filepath.Abs(filepath.Join(project, dir))
if err != nil || !strings.HasPrefix(base, filepath.Clean(project)+string(os.PathSeparator)) {
return nil, nil, nil, errors.New("invalid directory path")
}
entries, err := os.ReadDir(base)
Copilot is powered by AI and may make mistakes. Always verify output.
dir = "."
}
base := filepath.Join(project, dir)
entries, err := os.ReadDir(base)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 1 day ago

To fix the issue, the dir parameter must be validated to ensure it does not contain path traversal sequences (..) or absolute paths. Additionally, the resolved path should be checked to ensure it remains within the intended project directory.

Steps to implement the fix:

  1. Add validation logic to check for invalid characters or sequences in the dir parameter (e.g., .., /, \).
  2. Resolve the full path using filepath.Abs and verify that it is within the project root directory.
  3. Reject the request with an appropriate error message if validation fails.

Suggested changeset 2
pkg/scai/dirs/dirs.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/pkg/scai/dirs/dirs.go b/pkg/scai/dirs/dirs.go
--- a/pkg/scai/dirs/dirs.go
+++ b/pkg/scai/dirs/dirs.go
@@ -78,4 +78,12 @@
 	}
+	// Validate the `dir` parameter to prevent path traversal
+	if strings.Contains(dir, "..") || strings.Contains(dir, "/") || strings.Contains(dir, "\\") {
+		return nil, nil, nil, errors.New("invalid directory path")
+	}
 	base := filepath.Join(project, dir)
-	entries, err := os.ReadDir(base)
+	absBase, err := filepath.Abs(base)
+	if err != nil || !strings.HasPrefix(absBase, filepath.Clean(project)) {
+		return nil, nil, nil, errors.New("directory path is outside the project root")
+	}
+	entries, err := os.ReadDir(absBase)
 	if err != nil {
EOF
@@ -78,4 +78,12 @@
}
// Validate the `dir` parameter to prevent path traversal
if strings.Contains(dir, "..") || strings.Contains(dir, "/") || strings.Contains(dir, "\\") {
return nil, nil, nil, errors.New("invalid directory path")
}
base := filepath.Join(project, dir)
entries, err := os.ReadDir(base)
absBase, err := filepath.Abs(base)
if err != nil || !strings.HasPrefix(absBase, filepath.Clean(project)) {
return nil, nil, nil, errors.New("directory path is outside the project root")
}
entries, err := os.ReadDir(absBase)
if err != nil {
pkg/scai/dirs/handlers.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/pkg/scai/dirs/handlers.go b/pkg/scai/dirs/handlers.go
--- a/pkg/scai/dirs/handlers.go
+++ b/pkg/scai/dirs/handlers.go
@@ -195,2 +195,8 @@
 	}
+	// Validate the `dir` parameter to prevent path traversal
+	if strings.Contains(dir, "..") || strings.Contains(dir, "/") || strings.Contains(dir, "\\") {
+		return errors.NewValidationError("invalid directory path", map[string]interface{}{
+			"error": "path traversal detected",
+		})
+	}
 	projectRoot, err := h.getProjectRoot(r)
EOF
@@ -195,2 +195,8 @@
}
// Validate the `dir` parameter to prevent path traversal
if strings.Contains(dir, "..") || strings.Contains(dir, "/") || strings.Contains(dir, "\\") {
return errors.NewValidationError("invalid directory path", map[string]interface{}{
"error": "path traversal detected",
})
}
projectRoot, err := h.getProjectRoot(r)
Copilot is powered by AI and may make mistakes. Always verify output.
continue
}
dirPath := filepath.Join(base, name)
dirEntries, err := os.ReadDir(dirPath)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 1 day ago

To fix the issue, the user-provided dir parameter must be validated before being used in path construction. Validation should ensure that:

  1. The dir parameter does not contain path traversal sequences (..) or path separators (/ or \).
  2. The resolved path remains within the intended directory scope.

The best approach is to:

  1. Normalize the dir parameter using filepath.Clean.
  2. Check that the resolved path is within the project root directory using filepath.Abs and strings.HasPrefix.

Changes will be made to the ListEntries, ListDirs, CreateDir, and DeleteDir methods in pkg/scai/dirs/dirs.go to validate the dir parameter.


Suggested changeset 1
pkg/scai/dirs/dirs.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/pkg/scai/dirs/dirs.go b/pkg/scai/dirs/dirs.go
--- a/pkg/scai/dirs/dirs.go
+++ b/pkg/scai/dirs/dirs.go
@@ -33,5 +33,9 @@
 	}
-	// Scope to project root
+	dir = filepath.Clean(dir)
 	base := filepath.Join(project, dir)
-	entries, err := os.ReadDir(base)
+	absBase, err := filepath.Abs(base)
+	if err != nil || !strings.HasPrefix(absBase, project) {
+		return nil, errors.New("invalid directory path")
+	}
+	entries, err := os.ReadDir(absBase)
 	if err != nil {
@@ -55,4 +59,9 @@
 	}
+	dir = filepath.Clean(dir)
 	base := filepath.Join(project, dir)
-	return os.MkdirAll(base, 0755)
+	absBase, err := filepath.Abs(base)
+	if err != nil || !strings.HasPrefix(absBase, project) {
+		return errors.New("invalid directory path")
+	}
+	return os.MkdirAll(absBase, 0755)
 }
@@ -66,4 +75,9 @@
 	}
+	dir = filepath.Clean(dir)
 	base := filepath.Join(project, dir)
-	return os.RemoveAll(base)
+	absBase, err := filepath.Abs(base)
+	if err != nil || !strings.HasPrefix(absBase, project) {
+		return errors.New("invalid directory path")
+	}
+	return os.RemoveAll(absBase)
 }
@@ -78,4 +92,9 @@
 	}
+	dir = filepath.Clean(dir)
 	base := filepath.Join(project, dir)
-	entries, err := os.ReadDir(base)
+	absBase, err := filepath.Abs(base)
+	if err != nil || !strings.HasPrefix(absBase, project) {
+		return nil, nil, nil, errors.New("invalid directory path")
+	}
+	entries, err := os.ReadDir(absBase)
 	if err != nil {
EOF
@@ -33,5 +33,9 @@
}
// Scope to project root
dir = filepath.Clean(dir)
base := filepath.Join(project, dir)
entries, err := os.ReadDir(base)
absBase, err := filepath.Abs(base)
if err != nil || !strings.HasPrefix(absBase, project) {
return nil, errors.New("invalid directory path")
}
entries, err := os.ReadDir(absBase)
if err != nil {
@@ -55,4 +59,9 @@
}
dir = filepath.Clean(dir)
base := filepath.Join(project, dir)
return os.MkdirAll(base, 0755)
absBase, err := filepath.Abs(base)
if err != nil || !strings.HasPrefix(absBase, project) {
return errors.New("invalid directory path")
}
return os.MkdirAll(absBase, 0755)
}
@@ -66,4 +75,9 @@
}
dir = filepath.Clean(dir)
base := filepath.Join(project, dir)
return os.RemoveAll(base)
absBase, err := filepath.Abs(base)
if err != nil || !strings.HasPrefix(absBase, project) {
return errors.New("invalid directory path")
}
return os.RemoveAll(absBase)
}
@@ -78,4 +92,9 @@
}
dir = filepath.Clean(dir)
base := filepath.Join(project, dir)
entries, err := os.ReadDir(base)
absBase, err := filepath.Abs(base)
if err != nil || !strings.HasPrefix(absBase, project) {
return nil, nil, nil, errors.New("invalid directory path")
}
entries, err := os.ReadDir(absBase)
if err != nil {
Copilot is powered by AI and may make mistakes. Always verify output.
dir = "."
}
base := filepath.Join(project, dir)
entries, err := ioutil.ReadDir(base)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 2 days ago

To fix the issue, we need to validate the dir parameter before using it to construct a file path. The validation should ensure that:

  1. The dir parameter does not contain any path traversal sequences (..) or path separators (/ or \).
  2. The resolved path remains within the intended directory (project).

The best approach is to:

  1. Use filepath.Abs to resolve the absolute path of the constructed base directory.
  2. Check that the resolved path starts with the project directory using strings.HasPrefix.

Changes are required in the ListFiles method in pkg/scai/files/files.go.


Suggested changeset 1
pkg/scai/files/files.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/pkg/scai/files/files.go b/pkg/scai/files/files.go
--- a/pkg/scai/files/files.go
+++ b/pkg/scai/files/files.go
@@ -33,3 +33,7 @@
 	base := filepath.Join(project, dir)
-	entries, err := ioutil.ReadDir(base)
+	absBase, err := filepath.Abs(base)
+	if err != nil || !strings.HasPrefix(absBase, filepath.Clean(project)+string(os.PathSeparator)) {
+		return nil, errors.New("invalid directory path")
+	}
+	entries, err := ioutil.ReadDir(absBase)
 	if err != nil {
EOF
@@ -33,3 +33,7 @@
base := filepath.Join(project, dir)
entries, err := ioutil.ReadDir(base)
absBase, err := filepath.Abs(base)
if err != nil || !strings.HasPrefix(absBase, filepath.Clean(project)+string(os.PathSeparator)) {
return nil, errors.New("invalid directory path")
}
entries, err := ioutil.ReadDir(absBase)
if err != nil {
Copilot is powered by AI and may make mistakes. Always verify output.
return errors.New("path is required")
}
base := filepath.Join(project, path)
return os.Remove(base)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 2 days ago

To fix the issue, the path parameter must be validated before constructing the file path and performing the file operation. The validation should ensure:

  1. The path does not contain any ../ sequences or absolute paths.
  2. The resolved path remains within the intended directory (project).

The best approach is to use filepath.Abs to resolve the absolute path and verify that it starts with the project directory. If the validation fails, the method should return an error.

Changes are required in the DeleteFile method in pkg/scai/files/files.go.


Suggested changeset 1
pkg/scai/files/files.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/pkg/scai/files/files.go b/pkg/scai/files/files.go
--- a/pkg/scai/files/files.go
+++ b/pkg/scai/files/files.go
@@ -75,4 +75,9 @@
 	}
+	// Resolve the absolute path and validate it
 	base := filepath.Join(project, path)
-	return os.Remove(base)
+	absPath, err := filepath.Abs(base)
+	if err != nil || !strings.HasPrefix(absPath, filepath.Clean(project)+string(os.PathSeparator)) {
+		return errors.New("invalid path")
+	}
+	return os.Remove(absPath)
 }
EOF
@@ -75,4 +75,9 @@
}
// Resolve the absolute path and validate it
base := filepath.Join(project, path)
return os.Remove(base)
absPath, err := filepath.Abs(base)
if err != nil || !strings.HasPrefix(absPath, filepath.Clean(project)+string(os.PathSeparator)) {
return errors.New("invalid path")
}
return os.Remove(absPath)
}
Copilot is powered by AI and may make mistakes. Always verify output.
}
// Ensure git repository is initialized before committing
gitDir := filepath.Join(projectDst, ".git")
if _, err := os.Stat(gitDir); os.IsNotExist(err) {

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 1 day ago

To fix the issue, we need to validate the name input to ensure it does not contain any path traversal sequences or invalid characters. Additionally, we should verify that the constructed paths (projectDir and gitDir) remain within the intended base directory (s.ProjectsDir). This can be achieved by resolving the absolute path and checking that it starts with the base directory.

  1. Add validation to ensure that name does not contain invalid characters such as /, \, or ...
  2. After constructing projectDir and gitDir, resolve their absolute paths and verify that they are within s.ProjectsDir.
  3. If any validation fails, return an appropriate error.

Suggested changeset 1
pkg/scai/projects/service.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/pkg/scai/projects/service.go b/pkg/scai/projects/service.go
--- a/pkg/scai/projects/service.go
+++ b/pkg/scai/projects/service.go
@@ -143,2 +143,7 @@
 func (s *ProjectsService) CreateProject(ctx context.Context, name, description, boilerplate string, networkID *int64) (Project, error) {
+	// Validate the name to prevent invalid or malicious input
+	if strings.Contains(name, "/") || strings.Contains(name, "\\") || strings.Contains(name, "..") {
+		return Project{}, fmt.Errorf("invalid project name: %s", name)
+	}
+
 	slug, err := generateSlug(name, s.Queries, ctx)
@@ -163,3 +168,9 @@
 		projectDir := filepath.Join(s.ProjectsDir, slug)
-		if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, projectDir); err != nil {
+		// Ensure projectDir is within s.ProjectsDir
+		absProjectDir, err := filepath.Abs(projectDir)
+		if err != nil || !strings.HasPrefix(absProjectDir, filepath.Clean(s.ProjectsDir)+string(os.PathSeparator)) {
+			return Project{}, fmt.Errorf("invalid project directory: %s", projectDir)
+		}
+
+		if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, absProjectDir); err != nil {
 			zap.L().Error("failed to download boilerplate", zap.String("boilerplate", boilerplate), zap.Error(err))
@@ -169,6 +180,6 @@
 		// Ensure git repository is initialized before committing
-		gitDir := filepath.Join(projectDir, ".git")
+		gitDir := filepath.Join(absProjectDir, ".git")
 		if _, err := os.Stat(gitDir); os.IsNotExist(err) {
 			// Initialize the repo using go-git
-			_, err := versionmanagement.InitRepo(projectDir)
+			_, err := versionmanagement.InitRepo(absProjectDir)
 			if err != nil {
EOF
@@ -143,2 +143,7 @@
func (s *ProjectsService) CreateProject(ctx context.Context, name, description, boilerplate string, networkID *int64) (Project, error) {
// Validate the name to prevent invalid or malicious input
if strings.Contains(name, "/") || strings.Contains(name, "\\") || strings.Contains(name, "..") {
return Project{}, fmt.Errorf("invalid project name: %s", name)
}

slug, err := generateSlug(name, s.Queries, ctx)
@@ -163,3 +168,9 @@
projectDir := filepath.Join(s.ProjectsDir, slug)
if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, projectDir); err != nil {
// Ensure projectDir is within s.ProjectsDir
absProjectDir, err := filepath.Abs(projectDir)
if err != nil || !strings.HasPrefix(absProjectDir, filepath.Clean(s.ProjectsDir)+string(os.PathSeparator)) {
return Project{}, fmt.Errorf("invalid project directory: %s", projectDir)
}

if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, absProjectDir); err != nil {
zap.L().Error("failed to download boilerplate", zap.String("boilerplate", boilerplate), zap.Error(err))
@@ -169,6 +180,6 @@
// Ensure git repository is initialized before committing
gitDir := filepath.Join(projectDir, ".git")
gitDir := filepath.Join(absProjectDir, ".git")
if _, err := os.Stat(gitDir); os.IsNotExist(err) {
// Initialize the repo using go-git
_, err := versionmanagement.InitRepo(projectDir)
_, err := versionmanagement.InitRepo(absProjectDir)
if err != nil {
Copilot is powered by AI and may make mistakes. Always verify output.
- Updated `.gitignore` to include `projects-data-node1`, improving project cleanliness.
- Added new dependencies `github.com/google/go-github/v45` and `github.com/google/go-querystring` to `go.mod` for enhanced API functionalities.
- Updated Swagger documentation to reflect changes in API endpoints, including versioning and improved descriptions for better clarity.
- Enhanced SQL queries in `dev-queries.sql` to include `network_id` in project creation, ensuring better data management.

These changes improve the overall organization and usability of the application, providing clearer documentation and enhanced functionality for users.

Signed-off-by: David VIEJO <dviejo@kungfusoftware.es>
}

// Create the target directory if it doesn't exist
if err := os.MkdirAll(targetDir, 0755); 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 1 day ago

To fix the issue, we need to validate and sanitize the targetDir parameter before using it in file system operations. The best approach is to ensure that the resolved path is within a predefined safe directory. This involves:

  1. Defining a safe base directory (e.g., ProjectsDir).
  2. Resolving the targetDir relative to the safe base directory using filepath.Abs.
  3. Verifying that the resolved path starts with the safe base directory to prevent path traversal attacks.

Changes are required in pkg/scai/projects/service.go and pkg/scai/boilerplates/boilerplates.go to implement this validation.


Suggested changeset 2
pkg/scai/boilerplates/boilerplates.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/pkg/scai/boilerplates/boilerplates.go b/pkg/scai/boilerplates/boilerplates.go
--- a/pkg/scai/boilerplates/boilerplates.go
+++ b/pkg/scai/boilerplates/boilerplates.go
@@ -175,3 +175,7 @@
 	// Create the target directory if it doesn't exist
-	if err := os.MkdirAll(targetDir, 0755); err != nil {
+	absTargetDir, err := filepath.Abs(targetDir)
+	if err != nil || !strings.HasPrefix(absTargetDir, filepath.Clean(safeBaseDir)+string(os.PathSeparator)) {
+		return fmt.Errorf("invalid target directory: %s", targetDir)
+	}
+	if err := os.MkdirAll(absTargetDir, 0755); err != nil {
 		return fmt.Errorf("failed to create target directory: %w", err)
EOF
@@ -175,3 +175,7 @@
// Create the target directory if it doesn't exist
if err := os.MkdirAll(targetDir, 0755); err != nil {
absTargetDir, err := filepath.Abs(targetDir)
if err != nil || !strings.HasPrefix(absTargetDir, filepath.Clean(safeBaseDir)+string(os.PathSeparator)) {
return fmt.Errorf("invalid target directory: %s", targetDir)
}
if err := os.MkdirAll(absTargetDir, 0755); err != nil {
return fmt.Errorf("failed to create target directory: %w", err)
pkg/scai/projects/service.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/pkg/scai/projects/service.go b/pkg/scai/projects/service.go
--- a/pkg/scai/projects/service.go
+++ b/pkg/scai/projects/service.go
@@ -163,3 +163,8 @@
 		projectDir := filepath.Join(s.ProjectsDir, slug)
-		if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, projectDir); err != nil {
+		absProjectDir, err := filepath.Abs(projectDir)
+		if err != nil || !strings.HasPrefix(absProjectDir, filepath.Clean(s.ProjectsDir)+string(os.PathSeparator)) {
+			zap.L().Error("invalid project directory", zap.String("projectDir", projectDir), zap.Error(err))
+			return Project{}, fmt.Errorf("invalid project directory: %s", projectDir)
+		}
+		if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, absProjectDir); err != nil {
 			zap.L().Error("failed to download boilerplate", zap.String("boilerplate", boilerplate), zap.Error(err))
EOF
@@ -163,3 +163,8 @@
projectDir := filepath.Join(s.ProjectsDir, slug)
if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, projectDir); err != nil {
absProjectDir, err := filepath.Abs(projectDir)
if err != nil || !strings.HasPrefix(absProjectDir, filepath.Clean(s.ProjectsDir)+string(os.PathSeparator)) {
zap.L().Error("invalid project directory", zap.String("projectDir", projectDir), zap.Error(err))
return Project{}, fmt.Errorf("invalid project directory: %s", projectDir)
}
if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, absProjectDir); err != nil {
zap.L().Error("failed to download boilerplate", zap.String("boilerplate", boilerplate), zap.Error(err))
Copilot is powered by AI and may make mistakes. Always verify output.

// Extract the tarball
for {
header, err := tr.Next()

Check failure

Code scanning / CodeQL

Arbitrary file access during archive extraction ("Zip Slip") High

Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.

Copilot Autofix

AI 1 day ago

To fix the issue, we need to validate the header.Name field to ensure it does not contain directory traversal elements (..) and that the constructed targetPath remains within the intended targetDir. This can be achieved by:

  1. Using filepath.Clean to normalize the path and remove any traversal elements.
  2. Verifying that the resulting targetPath is within the targetDir using filepath.HasPrefix or equivalent logic.
  3. Rejecting any paths that fail these checks before proceeding with file system operations.

The fix will involve modifying the code where targetPath is constructed and adding validation logic.


Suggested changeset 1
pkg/scai/boilerplates/boilerplates.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/pkg/scai/boilerplates/boilerplates.go b/pkg/scai/boilerplates/boilerplates.go
--- a/pkg/scai/boilerplates/boilerplates.go
+++ b/pkg/scai/boilerplates/boilerplates.go
@@ -230,2 +230,8 @@
 
+		// Sanitize and validate the target path
+		cleanTargetPath := filepath.Clean(targetPath)
+		if !strings.HasPrefix(cleanTargetPath, targetDir) {
+			return fmt.Errorf("invalid file path: %s", header.Name)
+		}
+
 		switch header.Typeflag {
EOF
@@ -230,2 +230,8 @@

// Sanitize and validate the target path
cleanTargetPath := filepath.Clean(targetPath)
if !strings.HasPrefix(cleanTargetPath, targetDir) {
return fmt.Errorf("invalid file path: %s", header.Name)
}

switch header.Typeflag {
Copilot is powered by AI and may make mistakes. Always verify output.
switch header.Typeflag {
case tar.TypeDir:
// Create directory
if err := os.MkdirAll(targetPath, 0755); 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 1 day ago

To fix the issue, the user-controlled input (targetDir and targetPath) must be validated to ensure it does not contain malicious components such as ../ or absolute paths. The best approach is to:

  1. Resolve the absolute path of targetPath using filepath.Abs.
  2. Check that the resolved path is within a predefined safe directory (targetDir).
  3. Reject the input if it fails validation.

This ensures that the file system operations are restricted to a safe directory and prevents directory traversal attacks.


Suggested changeset 1
pkg/scai/boilerplates/boilerplates.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/pkg/scai/boilerplates/boilerplates.go b/pkg/scai/boilerplates/boilerplates.go
--- a/pkg/scai/boilerplates/boilerplates.go
+++ b/pkg/scai/boilerplates/boilerplates.go
@@ -230,2 +230,8 @@
 
+		// Validate that targetPath is within targetDir
+		absTargetPath, err := filepath.Abs(targetPath)
+		if err != nil || !strings.HasPrefix(absTargetPath, targetDir) {
+			return fmt.Errorf("invalid file path: %s", targetPath)
+		}
+
 		switch header.Typeflag {
@@ -233,4 +239,4 @@
 			// Create directory
-			if err := os.MkdirAll(targetPath, 0755); err != nil {
-				return fmt.Errorf("failed to create directory %s: %w", targetPath, err)
+			if err := os.MkdirAll(absTargetPath, 0755); err != nil {
+				return fmt.Errorf("failed to create directory %s: %w", absTargetPath, err)
 			}
@@ -238,4 +244,4 @@
 			// Create parent directories
-			if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
-				return fmt.Errorf("failed to create parent directory for %s: %w", targetPath, err)
+			if err := os.MkdirAll(filepath.Dir(absTargetPath), 0755); err != nil {
+				return fmt.Errorf("failed to create parent directory for %s: %w", absTargetPath, err)
 			}
@@ -243,5 +249,5 @@
 			// Create the file
-			file, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(header.Mode))
+			file, err := os.OpenFile(absTargetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(header.Mode))
 			if err != nil {
-				return fmt.Errorf("failed to create file %s: %w", targetPath, err)
+				return fmt.Errorf("failed to create file %s: %w", absTargetPath, err)
 			}
@@ -251,3 +257,3 @@
 				file.Close()
-				return fmt.Errorf("failed to write file %s: %w", targetPath, err)
+				return fmt.Errorf("failed to write file %s: %w", absTargetPath, err)
 			}
EOF
@@ -230,2 +230,8 @@

// Validate that targetPath is within targetDir
absTargetPath, err := filepath.Abs(targetPath)
if err != nil || !strings.HasPrefix(absTargetPath, targetDir) {
return fmt.Errorf("invalid file path: %s", targetPath)
}

switch header.Typeflag {
@@ -233,4 +239,4 @@
// Create directory
if err := os.MkdirAll(targetPath, 0755); err != nil {
return fmt.Errorf("failed to create directory %s: %w", targetPath, err)
if err := os.MkdirAll(absTargetPath, 0755); err != nil {
return fmt.Errorf("failed to create directory %s: %w", absTargetPath, err)
}
@@ -238,4 +244,4 @@
// Create parent directories
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
return fmt.Errorf("failed to create parent directory for %s: %w", targetPath, err)
if err := os.MkdirAll(filepath.Dir(absTargetPath), 0755); err != nil {
return fmt.Errorf("failed to create parent directory for %s: %w", absTargetPath, err)
}
@@ -243,5 +249,5 @@
// Create the file
file, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(header.Mode))
file, err := os.OpenFile(absTargetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(header.Mode))
if err != nil {
return fmt.Errorf("failed to create file %s: %w", targetPath, err)
return fmt.Errorf("failed to create file %s: %w", absTargetPath, err)
}
@@ -251,3 +257,3 @@
file.Close()
return fmt.Errorf("failed to write file %s: %w", targetPath, err)
return fmt.Errorf("failed to write file %s: %w", absTargetPath, err)
}
Copilot is powered by AI and may make mistakes. Always verify output.
}
case tar.TypeReg:
// Create parent directories
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); 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 1 day ago

To fix the issue, we need to validate and sanitize the targetPath to ensure it does not allow directory traversal. Specifically:

  1. Use filepath.Abs to resolve the absolute path of targetPath.
  2. Verify that the resolved targetPath is within the intended targetDir by checking that it has the correct prefix.
  3. Reject any paths that do not meet these criteria with an appropriate error message.

This fix ensures that even if the user provides malicious input, the code will not allow access to unintended parts of the file system.


Suggested changeset 1
pkg/scai/boilerplates/boilerplates.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/pkg/scai/boilerplates/boilerplates.go b/pkg/scai/boilerplates/boilerplates.go
--- a/pkg/scai/boilerplates/boilerplates.go
+++ b/pkg/scai/boilerplates/boilerplates.go
@@ -230,2 +230,8 @@
 
+		// Validate that targetPath is within targetDir
+		absTargetPath, err := filepath.Abs(targetPath)
+		if err != nil || !strings.HasPrefix(absTargetPath, filepath.Clean(targetDir)+string(os.PathSeparator)) {
+			return fmt.Errorf("invalid file path: %s", targetPath)
+		}
+
 		switch header.Typeflag {
EOF
@@ -230,2 +230,8 @@

// Validate that targetPath is within targetDir
absTargetPath, err := filepath.Abs(targetPath)
if err != nil || !strings.HasPrefix(absTargetPath, filepath.Clean(targetDir)+string(os.PathSeparator)) {
return fmt.Errorf("invalid file path: %s", targetPath)
}

switch header.Typeflag {
Copilot is powered by AI and may make mistakes. Always verify output.
}

// Create the file
file, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(header.Mode))

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 1 day ago

To fix the issue, we need to validate and sanitize the targetDir and targetPath to ensure they are safe before using them in file system operations. Specifically:

  1. Ensure that targetDir is resolved to an absolute path and verify that it is within a predefined safe directory.
  2. Validate targetPath to ensure it does not contain malicious path components like .. or absolute paths.
  3. Reject or sanitize any input that does not meet these criteria.

The changes will be made in pkg/scai/boilerplates/boilerplates.go within the DownloadBoilerplate function. We will introduce validation logic for targetDir and targetPath.


Suggested changeset 1
pkg/scai/boilerplates/boilerplates.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/pkg/scai/boilerplates/boilerplates.go b/pkg/scai/boilerplates/boilerplates.go
--- a/pkg/scai/boilerplates/boilerplates.go
+++ b/pkg/scai/boilerplates/boilerplates.go
@@ -174,4 +174,14 @@
 
+	// Resolve and validate the target directory
+	absTargetDir, err := filepath.Abs(targetDir)
+	if err != nil {
+		return fmt.Errorf("failed to resolve target directory: %w", err)
+	}
+	const safeBaseDir = "/safe/base/directory" // Replace with your actual safe directory
+	if !strings.HasPrefix(absTargetDir, safeBaseDir) {
+		return fmt.Errorf("target directory is outside the allowed base directory")
+	}
+
 	// Create the target directory if it doesn't exist
-	if err := os.MkdirAll(targetDir, 0755); err != nil {
+	if err := os.MkdirAll(absTargetDir, 0755); err != nil {
 		return fmt.Errorf("failed to create target directory: %w", err)
@@ -228,3 +238,11 @@
 		targetPath := strings.TrimPrefix(header.Name, fmt.Sprintf("%s-main/", config.RepoName))
-		targetPath = filepath.Join(targetDir, targetPath)
+		targetPath = filepath.Join(absTargetDir, targetPath)
+		// Validate the resolved target path
+		absTargetPath, err := filepath.Abs(targetPath)
+		if err != nil {
+			return fmt.Errorf("failed to resolve target path: %w", err)
+		}
+		if !strings.HasPrefix(absTargetPath, absTargetDir) {
+			return fmt.Errorf("target path is outside the allowed target directory")
+		}
 
EOF
@@ -174,4 +174,14 @@

// Resolve and validate the target directory
absTargetDir, err := filepath.Abs(targetDir)
if err != nil {
return fmt.Errorf("failed to resolve target directory: %w", err)
}
const safeBaseDir = "/safe/base/directory" // Replace with your actual safe directory
if !strings.HasPrefix(absTargetDir, safeBaseDir) {
return fmt.Errorf("target directory is outside the allowed base directory")
}

// Create the target directory if it doesn't exist
if err := os.MkdirAll(targetDir, 0755); err != nil {
if err := os.MkdirAll(absTargetDir, 0755); err != nil {
return fmt.Errorf("failed to create target directory: %w", err)
@@ -228,3 +238,11 @@
targetPath := strings.TrimPrefix(header.Name, fmt.Sprintf("%s-main/", config.RepoName))
targetPath = filepath.Join(targetDir, targetPath)
targetPath = filepath.Join(absTargetDir, targetPath)
// Validate the resolved target path
absTargetPath, err := filepath.Abs(targetPath)
if err != nil {
return fmt.Errorf("failed to resolve target path: %w", err)
}
if !strings.HasPrefix(absTargetPath, absTargetDir) {
return fmt.Errorf("target path is outside the allowed target directory")
}

Copilot is powered by AI and may make mistakes. Always verify output.
}
vm := versionmanagement.NewDefaultManager()
cwd, _ := os.Getwd()
if err := os.Chdir(projectDir); 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 1 day ago

To fix the issue, the name parameter should be validated before it is used to construct the slug and subsequently the projectDir. Validation should ensure that the name does not contain any path traversal sequences (../) or path separators (/ or \). Additionally, the constructed projectDir should be verified to ensure it remains within the intended ProjectsDir directory.

Steps to implement the fix:

  1. Add validation logic to check the name parameter for forbidden characters or sequences.
  2. Ensure that the projectDir path is resolved and verified to be within the ProjectsDir directory before any file system operations are performed.
  3. Update the CreateProject method in pkg/scai/projects/service.go to include these validations.
Suggested changeset 1
pkg/scai/projects/service.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/pkg/scai/projects/service.go b/pkg/scai/projects/service.go
--- a/pkg/scai/projects/service.go
+++ b/pkg/scai/projects/service.go
@@ -143,2 +143,7 @@
 func (s *ProjectsService) CreateProject(ctx context.Context, name, description, boilerplate string, networkID *int64) (Project, error) {
+	// Validate name to prevent path traversal
+	if strings.Contains(name, "/") || strings.Contains(name, "\\") || strings.Contains(name, "..") {
+		return Project{}, errors.New("invalid project name")
+	}
+
 	slug, err := generateSlug(name, s.Queries, ctx)
@@ -163,3 +168,9 @@
 		projectDir := filepath.Join(s.ProjectsDir, slug)
-		if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, projectDir); err != nil {
+		// Ensure projectDir is within ProjectsDir
+		absProjectDir, err := filepath.Abs(projectDir)
+		if err != nil || !strings.HasPrefix(absProjectDir, filepath.Clean(s.ProjectsDir)+string(os.PathSeparator)) {
+			return Project{}, errors.New("invalid project directory")
+		}
+
+		if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, absProjectDir); err != nil {
 			zap.L().Error("failed to download boilerplate", zap.String("boilerplate", boilerplate), zap.Error(err))
@@ -169,6 +180,6 @@
 		// Ensure git repository is initialized before committing
-		gitDir := filepath.Join(projectDir, ".git")
+		gitDir := filepath.Join(absProjectDir, ".git")
 		if _, err := os.Stat(gitDir); os.IsNotExist(err) {
 			// Initialize the repo using go-git
-			_, err := versionmanagement.InitRepo(projectDir)
+			_, err := versionmanagement.InitRepo(absProjectDir)
 			if err != nil {
@@ -179,3 +190,3 @@
 		cwd, _ := os.Getwd()
-		if err := os.Chdir(projectDir); err == nil {
+		if err := os.Chdir(absProjectDir); err == nil {
 			err = vm.CommitChange(ctx, "Initial commit for project "+name)
EOF
@@ -143,2 +143,7 @@
func (s *ProjectsService) CreateProject(ctx context.Context, name, description, boilerplate string, networkID *int64) (Project, error) {
// Validate name to prevent path traversal
if strings.Contains(name, "/") || strings.Contains(name, "\\") || strings.Contains(name, "..") {
return Project{}, errors.New("invalid project name")
}

slug, err := generateSlug(name, s.Queries, ctx)
@@ -163,3 +168,9 @@
projectDir := filepath.Join(s.ProjectsDir, slug)
if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, projectDir); err != nil {
// Ensure projectDir is within ProjectsDir
absProjectDir, err := filepath.Abs(projectDir)
if err != nil || !strings.HasPrefix(absProjectDir, filepath.Clean(s.ProjectsDir)+string(os.PathSeparator)) {
return Project{}, errors.New("invalid project directory")
}

if err := s.BoilerplateService.DownloadBoilerplate(ctx, boilerplate, absProjectDir); err != nil {
zap.L().Error("failed to download boilerplate", zap.String("boilerplate", boilerplate), zap.Error(err))
@@ -169,6 +180,6 @@
// Ensure git repository is initialized before committing
gitDir := filepath.Join(projectDir, ".git")
gitDir := filepath.Join(absProjectDir, ".git")
if _, err := os.Stat(gitDir); os.IsNotExist(err) {
// Initialize the repo using go-git
_, err := versionmanagement.InitRepo(projectDir)
_, err := versionmanagement.InitRepo(absProjectDir)
if err != nil {
@@ -179,3 +190,3 @@
cwd, _ := os.Getwd()
if err := os.Chdir(projectDir); err == nil {
if err := os.Chdir(absProjectDir); err == nil {
err = vm.CommitChange(ctx, "Initial commit for project "+name)
Copilot is powered by AI and may make mistakes. Always verify output.
- Updated `go.mod` to include new dependencies for AI functionalities, including `github.com/anthropics/anthropic-sdk-go` and `github.com/openai/openai-go`.
- Refactored the HTTP server setup to utilize a new command structure, enhancing modularity and organization.
- Implemented AI client adapters for both OpenAI and Claude, ensuring compatibility with the new service layer.
- Introduced a new database migration management system using embedded SQL migrations, improving database setup processes.
- Enhanced project lifecycle management with new platform-specific lifecycle hooks for Fabric, improving project handling.
- Removed outdated test files and refactored existing AI tests to align with the new structure.

These changes significantly improve the application's architecture, maintainability, and functionality, ensuring a smooth transition from NestJS to Go while preserving existing features.

Signed-off-by: David VIEJO <dviejo@kungfusoftware.es>
- Updated the `ProjectsService` to include new dependencies for organization, key management, and network services, improving modularity and functionality.
- Refactored the `StartProjectServer` and `StopProjectServer` methods to utilize context for better error handling and lifecycle management.
- Enhanced the `FabricLifecycle` implementation to support additional parameters and improve the pre-start and post-start hooks, ensuring better integration with the project lifecycle.
- Introduced new methods for managing project lifecycle hooks, allowing for more flexible handling of platform-specific operations.

These changes significantly improve the application's architecture and maintainability, ensuring a smoother transition from NestJS to Go while preserving existing features.

Signed-off-by: David VIEJO <dviejo@kungfusoftware.es>
Signed-off-by: David VIEJO <dviejo@kungfusoftware.es>
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.

1 participant