Skip to content
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

Added zip file upload feature #353

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

Conversation

PaperBoardOfficial
Copy link
Contributor

First of all @yamadashy , your repo is neat and clean, its easy to run with docker. Great work. Here are my changes. (you check this also: https://youtu.be/VjBGvu9cECo)
Issue link: #310
Changes done:
Client:

  • added TryItFileUpload.vue
  • moved the shared button to PackButton.vue
  • shifted the pack button below the url input field or zip upload box.

Server:

  • added processZipFile.ts and fileUtils.ts
  • added adm-zip package
  • changed zod schema for validation of both url and file
  • remoteRepo.ts and processZipFile.ts both share the same instance of ratelimiter and cache

Way forward:
I didn't implement folder uploading. We can create a separate ticket for that.

Ping me if you have any suggestions.

Checklist

  • Run npm run test
  • Run npm run lint

Copy link

codecov bot commented Feb 13, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 89.44%. Comparing base (2f0c679) to head (87d7204).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #353   +/-   ##
=======================================
  Coverage   89.44%   89.44%           
=======================================
  Files          66       66           
  Lines        2917     2917           
  Branches      594      594           
=======================================
  Hits         2609     2609           
  Misses        308      308           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

coderabbitai bot commented Feb 13, 2025

📝 Walkthrough

Walkthrough

The pull request introduces several new features and modifications across both client and server code. A new Vue component named PackButton is added, which is integrated into the TryItUrlInput.vue component. The TryIt.vue component now supports dual input modes: URL input and file upload via the newly imported TryItFileUpload component. On the server side, the API endpoint /api/pack is updated to support file uploads through form-data, incorporating a new optional file property in the PackRequest interface. Additional functionalities include a new ZIP file processing function (processZipFile), updates to validation schemas to accommodate file uploads, and utility functions for ZIP extraction and temporary directory management. Shared caching and rate limiting instances are now imported from a centralized module, and several dependencies have been added or updated.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant T as TryIt Component
    participant F as File/URL Input Component
    participant P as PackButton
    participant API as API Server

    U->>T: Select mode (URL/File)
    alt File Mode
        T->>F: Render TryItFileUpload interface
        U->>F: Upload a file
        F->>T: Emit file upload event
    else URL Mode
        U->>F: Enter repository URL
    end
    U->>P: Click PackButton
    P->>T: Trigger submission
    T->>API: Send FormData with file (if exists) or URL
Loading
sequenceDiagram
    participant C as Client Request
    participant S as /api/pack Endpoint
    participant PZF as processZipFile
    participant PRR as processRemoteRepo
    participant U as Utility Functions

    C->>S: Submit FormData (file or URL)
    alt File Provided
        S->>PZF: Invoke processZipFile(file, format, options, clientIp)
        PZF->>U: Validate file, extract ZIP, manage temp directory
        U->>PZF: Return processed data
        PZF->>S: Return PackResult
    else URL Provided
        S->>PRR: Invoke processRemoteRepo(url, format, options, clientIp)
        PRR->>S: Return PackResult
    end
    S->>C: Respond with PackResult
Loading

Possibly related PRs

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (15)
website/server/src/index.ts (2)

48-50: Consider standardizing error format
You return plain text (“File size too large :(”) instead of a JSON response. Elsewhere, errors are returned as JSON. It might be preferable to standardize this for consistency.


61-65: Validate the file type
Currently, the code only checks if file is present or not. Consider verifying if it’s indeed a ZIP file or returning a more descriptive error if the content type is invalid.

website/server/src/utils/fileUtils.ts (2)

8-53: Robust ZIP extraction with security checks
Your approach to verifying file size, uncompressed size, file count, and disallowing directory traversal is excellent. One minor improvement: consider using path.resolve and comparing with destPath to ensure reliable cross-platform validation.


64-76: Validate directory path more thoroughly
Checking for the repomix- substring is good, but more robust checks (e.g., verifying the full absolute path) could be beneficial in guarding against sabotage or accidental misuse.

website/server/src/processZipFile.ts (2)

24-28: Schema validation overshadowing
Using validateRequest(packRequestSchema, { file, format, options }) is a good approach. Just ensure that any internal constraints (e.g., verifying file is indeed a ZIP) are also enforced or delegated to extractZip to avoid duplication.


40-45: Cache key generation
Combining file name, size, and lastModified is usually sufficient for smaller uploads. For larger or repeated uploads with the same name/size, consider hashing the file content for uniqueness.

website/server/src/utils/sharedInstance.ts (1)

6-7:

❓ Verification inconclusive

Consider extracting configuration values to environment variables.

The cache duration and rate limit values are hardcoded, making them difficult to adjust across different environments (development, staging, production).

Apply this diff to make the values configurable:

-export const cache = new RequestCache<PackResult>(180); // 3 minutes cache
-export const rateLimiter = new RateLimiter(60_000, 3); // 3 requests per minute
+const CACHE_DURATION_SECONDS = process.env.CACHE_DURATION_SECONDS || 180;
+const RATE_LIMIT_WINDOW_MS = process.env.RATE_LIMIT_WINDOW_MS || 60_000;
+const RATE_LIMIT_MAX_REQUESTS = process.env.RATE_LIMIT_MAX_REQUESTS || 3;
+
+export const cache = new RequestCache<PackResult>(CACHE_DURATION_SECONDS);
+export const rateLimiter = new RateLimiter(RATE_LIMIT_WINDOW_MS, RATE_LIMIT_MAX_REQUESTS);

Consider increasing the rate limit.

The current rate limit of 3 requests per minute might be too restrictive for legitimate users, especially when they need to make multiple attempts to get their configuration right.

Let's analyze the current rate limit configuration:


🏁 Script executed:

#!/bin/bash
# Description: Check if there are any rate limit related issues in GitHub issues
gh issue list --search "rate limit" --state all --json number,title,state

Length of output: 164


Attention: Environment Configuration & Rate Limit Consideration
Extract the cache duration and rate limit values to environment variables for improved configurability across environments. The refactor diff below shows how to move the hardcoded values into environment-configurable constants:

-export const cache = new RequestCache<PackResult>(180); // 3 minutes cache
-export const rateLimiter = new RateLimiter(60_000, 3); // 3 requests per minute
+const CACHE_DURATION_SECONDS = process.env.CACHE_DURATION_SECONDS || 180;
+const RATE_LIMIT_WINDOW_MS = process.env.RATE_LIMIT_WINDOW_MS || 60_000;
+const RATE_LIMIT_MAX_REQUESTS = process.env.RATE_LIMIT_MAX_REQUESTS || 3;
+
+export const cache = new RequestCache<PackResult>(CACHE_DURATION_SECONDS);
+export const rateLimiter = new RateLimiter(RATE_LIMIT_WINDOW_MS, RATE_LIMIT_MAX_REQUESTS);

Additionally, note that the current rate limit of 3 requests per minute might be too restrictive for users who may need multiple attempts to fine-tune their configuration. Although an automated check for rate-limit issues wasn’t conclusive (the verification command failed due to a missing git repository context), please manually verify if increasing the rate limit is needed based on your usage patterns.

website/server/src/constants.ts (1)

9-11: Improve file size formatting precision.

The current implementation might display too many decimal places. Consider rounding to 2 decimal places for better readability.

Apply this diff to improve the formatting:

-  return `${bytes / 1024 / 1024}MB`;
+  return `${(bytes / 1024 / 1024).toFixed(2)}MB`;
website/client/components/PackButton.vue (2)

9-21: Enhance SVG accessibility.

The SVG icon should have ARIA attributes and a title for better accessibility.

Apply this diff to improve accessibility:

       <svg v-if="!loading"
         class="pack-button-icon"
         width="20"
         height="20"
         viewBox="96.259 93.171 300 300"
+        role="img"
+        aria-hidden="false"
       >
+        <title>Pack icon</title>
         <g transform="matrix(1.160932, 0, 0, 1.160932, 97.635941, 94.725143)">
           <path
             fill="currentColor"
             d="M 128.03 -1.486 L 21.879 65.349 L 21.848 190.25 L 127.979 256.927 L 234.2 190.27 L 234.197 65.463 L 128.03 -1.486 Z M 208.832 70.323 L 127.984 121.129 L 47.173 70.323 L 128.144 19.57 L 208.832 70.323 Z M 39.669 86.367 L 119.188 136.415 L 119.255 230.529 L 39.637 180.386 L 39.669 86.367 Z M 136.896 230.506 L 136.887 136.575 L 216.469 86.192 L 216.417 180.46 L 136.896 230.506 Z M 136.622 230.849"
           />
         </g>
       </svg>

34-64: Consider scoping the styles.

The styles should be scoped to prevent potential conflicts with other components.

Add the scoped attribute to the style tag:

-<style>
+<style scoped>
website/server/src/schemas/request.ts (1)

30-32: Consider enhancing zip file validation.

While checking file type and extension is good, consider adding magic number validation for more reliable zip file detection.

 const isValidZipFile = (file: File) => {
-  return file.type === 'application/zip' || file.name.endsWith('.zip');
+  const isZipMimeOrExt = file.type === 'application/zip' || file.name.endsWith('.zip');
+  // Add magic number check when browser File API supports it
+  return isZipMimeOrExt;
 };
website/server/src/remoteRepo.ts (1)

30-32: Improve error message clarity for missing URL.

The error message should acknowledge that either a URL or file upload is required, making it clearer for API consumers.

-    throw new AppError('Repository URL is required for remote processing', 400);
+    throw new AppError('Either a repository URL or a ZIP file upload is required', 400);
website/client/components/TryItFileUpload.vue (2)

54-60: Improve accessibility for file input.

The file input lacks proper ARIA attributes and keyboard navigation support.

       <input
         ref="fileInput"
         type="file"
         accept=".zip"
         class="hidden-input"
+        aria-label="Choose ZIP file"
+        role="button"
+        tabindex="0"
         @change="(e) => handleFileSelect((e.target as HTMLInputElement).files)"
       />

83-86: Add loading state feedback during file processing.

The PackButton shows loading state but users might benefit from additional visual feedback in the upload area.

       <PackButton
         :loading="loading"
         :isValid="!!selectedFile"
+        :disabled="loading"
       />

Also, consider adding a loading overlay to the upload area:

     <div class="upload-container"
-      :class="{ 'drag-active': dragActive, 'has-error': errorMessage }"
+      :class="{ 
+        'drag-active': dragActive, 
+        'has-error': errorMessage,
+        'is-loading': loading 
+      }"
website/client/components/TryIt.vue (1)

87-91: Update keyboard handling for file upload mode.

The Enter key handler only works in URL mode. Consider updating it to work with file uploads as well.

 function handleKeydown(event: KeyboardEvent) {
-  if (event.key === 'Enter' && url.value && !loading.value) {
+  if (event.key === 'Enter' && !loading.value && 
+      ((mode.value === 'url' && url.value) || 
+       (mode.value === 'file' && uploadedFile.value))) {
     handleSubmit();
   }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71fca1e and cbdd595.

📒 Files selected for processing (15)
  • website/client/components/PackButton.vue (1 hunks)
  • website/client/components/TryIt.vue (5 hunks)
  • website/client/components/TryItFileUpload.vue (1 hunks)
  • website/client/components/TryItUrlInput.vue (3 hunks)
  • website/client/components/api/client.ts (2 hunks)
  • website/client/components/utils/requestHandlers.ts (3 hunks)
  • website/server/package.json (1 hunks)
  • website/server/src/constants.ts (1 hunks)
  • website/server/src/index.ts (3 hunks)
  • website/server/src/processZipFile.ts (1 hunks)
  • website/server/src/remoteRepo.ts (2 hunks)
  • website/server/src/schemas/request.ts (1 hunks)
  • website/server/src/utils/cache.ts (1 hunks)
  • website/server/src/utils/fileUtils.ts (1 hunks)
  • website/server/src/utils/sharedInstance.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: Test (macos-latest, 23.x)
  • GitHub Check: Test (macos-latest, 22.x)
  • GitHub Check: Test (windows-latest, 23.x)
  • GitHub Check: Test (windows-latest, 22.x)
  • GitHub Check: Test (windows-latest, 21.x)
  • GitHub Check: Test (windows-latest, 20.x)
  • GitHub Check: Test (windows-latest, 19.x)
  • GitHub Check: Test (windows-latest, 18.x)
  • GitHub Check: Test (windows-latest, 18.0.0)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (16)
website/server/src/index.ts (3)

8-11: Imports look good
These imports properly consolidate the file size checks and ZIP processing logic. Great step in modularizing and keeping the code DRY.


55-58: Handle potential invalid JSON for 'options'
If formData.get('options') is not valid JSON, JSON.parse() will throw an unhandled exception. Adding a try/catch or fallback behavior could prevent runtime errors.


77-85: Well-structured fallback logic
Cleanly routing to processZipFile or processRemoteRepo based on file vs. URL input is clear and straightforward. Good job!

website/server/src/utils/fileUtils.ts (2)

55-62: Well-encapsulated temp directory creation
This function simplifies the logic of creating a unique temporary directory. Looks good.


78-101: Ensure correct file copying
This function properly sanitizes filenames, checks source existence, and copies the file while creating the target directory. The error handling patterns look consistent and robust.

website/server/src/processZipFile.ts (1)

78-85: Neat integration with runDefaultAction
The workflow of extracting the ZIP, running the default action, and copying the output is well-structured and easy to follow. Nicely done!

website/client/components/utils/requestHandlers.ts (2)

5-10: LGTM! Interface update is clean and backward compatible.

The addition of the optional file property to RequestHandlerOptions maintains backward compatibility while extending functionality for file uploads.


21-21: LGTM! Request handling is properly updated.

The changes correctly integrate file handling into the existing request flow while maintaining the original functionality.

Also applies to: 32-33

website/server/src/utils/cache.ts (1)

70-81: LGTM! Cache key generation is well-designed.

The changes to generateCacheKey are well thought out:

  • Renamed parameter from 'url' to 'identifier' to be more generic
  • Added 'type' parameter to distinguish between URL and file caches
  • Maintains simple and efficient implementation
website/server/src/schemas/request.ts (2)

34-45: LGTM! File validation schema is well-structured.

The file validation schema is comprehensive:

  • Validates File instance
  • Checks file type
  • Enforces reasonable size limit (10MB)

47-66: LGTM! Request schema ensures valid input combinations.

The schema refinements effectively prevent invalid states:

  • Either URL or file must be provided
  • Both URL and file cannot be provided simultaneously
website/client/components/TryItUrlInput.vue (2)

52-54: LGTM! Clean integration of PackButton component.

The PackButton integration is clean and maintains the component's functionality while improving modularity.


102-107: LGTM! Button container styling is well-designed.

The styling for the pack button container:

  • Maintains proper spacing with margin-top
  • Ensures centered alignment
  • Preserves responsive design
website/server/src/remoteRepo.ts (1)

35-35:

✅ Verification successful

Verify cache key uniqueness between URL and file processing.

The cache key generation now includes a 'url' type parameter. Let's verify that this doesn't conflict with file-based cache keys.


🏁 Script executed:

#!/bin/bash
# Search for all cache key generation calls to ensure unique types
rg "generateCacheKey.*['\"](?:url|file)['\"]" -A 2

Length of output: 307


Cache Key Uniqueness Verified – No Conflicts Detected

The cache key generation in website/server/src/remoteRepo.ts now explicitly specifies the 'url' type, and our grep search confirms that no file-based cache keys (i.e., with 'file') are being generated in this context. There’s no apparent overlap between URL and file cache keys.

website/server/package.json (2)

16-16:

✅ Verification successful

Verify compatibility with updated repomix version.

Check the changelog for breaking changes in the new version of repomix.


🏁 Script executed:

#!/bin/bash
# Check for breaking changes in repomix
gh api repos/yamadashy/repomix/releases/tags/v0.2.26 --jq .body

Length of output: 1870


Compatibility confirmed with repomix v0.2.26

The changelog for repomix v0.2.26 shows several feature enhancements and improvements (e.g., support for multiple directories, the new --quiet CLI option, output enhancements, and improved Git URL parsing) without indicating any breaking changes. The dependency update in website/server/package.json appears safe, but please verify that the new features don’t inadvertently affect your current usage.


13-13:

✅ Verification successful

Verify security and compatibility of adm-zip package.

Let's check if this version of adm-zip has any known vulnerabilities and is compatible with the project's Node.js version.


🌐 Web query:

Are there any security vulnerabilities in adm-zip version 0.5.16?

💡 Result:

adm-zip version 0.5.16 does not appear to have any known security vulnerabilities according to available security databases[9][10]. This version was released on August 30, 2024[4], and multiple sources indicate:

  • Snyk's vulnerability database shows no direct vulnerabilities for [email protected][9]
  • ReversingLabs Spectra Assure Community scan found no malware or risks[10]
  • The vulnerability SNYK-JS-ADMZIP-1065796 (high severity) was fixed in [email protected][1][4], meaning all subsequent versions including 0.5.16 contain this fix
  • Earlier vulnerabilities like CVE-2018-1002204 (directory traversal) only affected versions <0.4.9[8]

Security researchers recommend using [email protected] rather than older versions like 0.4.x due to multiple security fixes implemented since 2021[1][4]. The latest version (0.5.16 at time of writing) appears to be secure based on current vulnerability reports[9][10].

Citations:


Security and Compatibility Verification Completed

  • Security: The security databases confirm that [email protected] has no known vulnerabilities. All major issues from prior versions have been addressed, so this version meets our security requirements.
  • Compatibility: There are no reported Node.js compatibility issues with this version. It is advisable to ensure that your project's Node.js version aligns with those commonly used with adm-zip.

Overall, the changes in website/server/package.json (line 13) are safe.

website/client/components/api/client.ts Show resolved Hide resolved
website/client/components/TryItFileUpload.vue Show resolved Hide resolved
website/client/components/TryIt.vue Show resolved Hide resolved
@yamadashy
Copy link
Owner

Hi, @PaperBoardOfficial !
Thank you for this amazing PR and the demo video!

I've tested it and everything works perfectly. The implementation is very well thought out and clean. I really appreciate how you've maintained the code quality and security standards.

I might add a commit later to adjust some design elements to match my preferences, but the functionality is spot on!

I'll review the code in detail later, but so far it looks great. Thank you for your contribution! 🚀

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (10)
website/client/components/TryItUrlInput.vue (1)

102-107: Consider adjusting the pack button container for mobile screens.

While the desktop styling looks good, consider adjusting the margin and width for mobile screens to ensure optimal layout.

Add these styles to the existing mobile media query:

 @media (max-width: 640px) {
   .url-input-container {
     flex-direction: column;
   }
+  .pack-button-container {
+    margin-top: 12px;  /* Reduced margin for tighter mobile layout */
+  }
 }
website/server/src/schemas/request.ts (1)

30-32: Enhance ZIP file validation.

The current validation only checks MIME type and file extension. Consider adding magic number validation for more robust ZIP file detection.

 const isValidZipFile = (file: File) => {
-  return file.type === 'application/zip' || file.name.endsWith('.zip');
+  const isZipType = file.type === 'application/zip' || file.name.endsWith('.zip');
+  // Add magic number check for ZIP files (first 4 bytes: 50 4B 03 04)
+  return isZipType && file.slice(0, 4).arrayBuffer().then(buffer => 
+    new Uint8Array(buffer)[0] === 0x50 && 
+    new Uint8Array(buffer)[1] === 0x4B && 
+    new Uint8Array(buffer)[2] === 0x03 && 
+    new Uint8Array(buffer)[3] === 0x04
+  );
 };
website/server/src/index.ts (1)

55-67: Consider adding content type validation.

While the form data handling is good, it's important to validate the content type of the request.

+  // Validate content type
+  const contentType = c.req.header('content-type');
+  if (!contentType?.includes('multipart/form-data')) {
+    return c.json({ error: 'Invalid content type. Expected multipart/form-data' } as ErrorResponse, 400);
+  }
+
   const formData = await c.req.formData();
website/server/src/utils/fileUtils.ts (1)

78-101: Consider adding file size check during copy.

While the path sanitization is good, consider adding a size check during the copy operation.

   // Verify source exists
   await fs.access(sourcePath);
+
+  // Check file size before copy
+  const stats = await fs.stat(sourcePath);
+  if (stats.size > FILE_SIZE_LIMITS.MAX_OUTPUT_SIZE) {
+    throw new AppError(`Output file size exceeds maximum limit of ${formatFileSize(FILE_SIZE_LIMITS.MAX_OUTPUT_SIZE)}`);
+  }
website/client/components/TryItFileUpload.vue (2)

14-14: Remove unused ref.

The isValidUrl ref is defined but never used in the component.

-const isValidUrl = ref(false);

48-53: Enhance drag and drop UX with file validation feedback.

Consider adding visual feedback when an invalid file is dragged over the drop zone.

 <div class="upload-container"
   :class="{
     'drag-active': dragActive,
-    'has-error': errorMessage
+    'has-error': errorMessage,
+    'invalid-file': dragActive && $event.dataTransfer?.items?.[0]?.type !== 'application/zip'
   }"
   @dragover.prevent="dragActive = true"
   @dragleave="dragActive = false"
   @drop.prevent="handleFileSelect($event.dataTransfer?.files || null)"
 >
website/server/src/processZipFile.ts (2)

75-111: Improve error handling and logging for file processing.

Consider enhancing the error handling:

  1. Add specific error types for different failure scenarios
  2. Implement structured logging with correlation IDs
  3. Add retry logic for transient failures
 const tempDirPath = await createTempDirectory();
+const correlationId = randomUUID();
+console.log(`[${correlationId}] Starting file processing for ${file.name}`);
 
 try {
   // Extract the ZIP file to the temporary directory
+  console.log(`[${correlationId}] Extracting ZIP file to ${tempDirPath}`);
   await extractZip(file, tempDirPath);
 
   // Execute default action on the extracted directory
+  console.log(`[${correlationId}] Running default action with options:`, cliOptions);
   const result = await runDefaultAction([tempDirPath], tempDirPath, cliOptions);

122-131: Good cleanup implementation!

The cleanup logic is well-implemented in the finally block. Consider adding a timeout for the cleanup operations to prevent hanging.

 } finally {
+  const cleanup = async () => {
+    await Promise.race([
+      cleanupTempDirectory(tempDirPath),
+      new Promise((_, reject) => setTimeout(() => reject(new Error('Cleanup timeout')), 5000))
+    ]);
+  };
+  
   try {
-    await fs.unlink(outputFilePath);
+    await cleanup();
   } catch (err) {
     console.warn('Failed to cleanup:', err);
   }
 }
website/client/components/TryIt.vue (2)

33-37: Improve type safety for mode validation.

Consider using a type guard to ensure type safety when checking the mode.

-if (mode.value === 'url' && !url.value) return;
-if (mode.value === 'file' && !uploadedFile.value) return;
+function isUrlMode(mode: string): mode is 'url' {
+  return mode === 'url';
+}
+
+if (isUrlMode(mode.value) && !url.value) return;
+if (!isUrlMode(mode.value) && !uploadedFile.value) return;

102-117: Enhance accessibility for mode switching buttons.

Add ARIA labels and roles to improve accessibility.

 <div class="input-mode-selector">
   <button 
     type="button" 
     :class="{ active: mode === 'url' }"
+    role="tab"
+    :aria-selected="mode === 'url'"
+    aria-controls="url-input"
     @click="mode = 'url'"
   >
     URL Input
   </button>
   <button 
     type="button" 
     :class="{ active: mode === 'file' }"
+    role="tab"
+    :aria-selected="mode === 'file'"
+    aria-controls="file-upload"
     @click="mode = 'file'"
   >
     File Upload
   </button>
 </div>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cbdd595 and 87d7204.

📒 Files selected for processing (15)
  • website/client/components/PackButton.vue (1 hunks)
  • website/client/components/TryIt.vue (5 hunks)
  • website/client/components/TryItFileUpload.vue (1 hunks)
  • website/client/components/TryItUrlInput.vue (3 hunks)
  • website/client/components/api/client.ts (2 hunks)
  • website/client/components/utils/requestHandlers.ts (3 hunks)
  • website/server/package.json (1 hunks)
  • website/server/src/constants.ts (1 hunks)
  • website/server/src/index.ts (3 hunks)
  • website/server/src/processZipFile.ts (1 hunks)
  • website/server/src/remoteRepo.ts (2 hunks)
  • website/server/src/schemas/request.ts (1 hunks)
  • website/server/src/utils/cache.ts (1 hunks)
  • website/server/src/utils/fileUtils.ts (1 hunks)
  • website/server/src/utils/sharedInstance.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • website/server/src/utils/sharedInstance.ts
  • website/server/package.json
  • website/client/components/api/client.ts
  • website/client/components/utils/requestHandlers.ts
  • website/server/src/constants.ts
  • website/server/src/utils/cache.ts
  • website/client/components/PackButton.vue
⏰ Context from checks skipped due to timeout of 90000ms (12)
  • GitHub Check: Test (macos-latest, 23.x)
  • GitHub Check: Test (macos-latest, 22.x)
  • GitHub Check: Test (macos-latest, 21.x)
  • GitHub Check: Test (windows-latest, 23.x)
  • GitHub Check: Test (windows-latest, 22.x)
  • GitHub Check: Build and run (windows-latest, 22.x)
  • GitHub Check: Test (windows-latest, 21.x)
  • GitHub Check: Test (windows-latest, 20.x)
  • GitHub Check: Test (windows-latest, 19.x)
  • GitHub Check: Test (windows-latest, 18.x)
  • GitHub Check: Test (windows-latest, 18.0.0)
  • GitHub Check: Build and run (windows-latest, 18.0.0)
🔇 Additional comments (13)
website/client/components/TryItUrlInput.vue (2)

4-4: LGTM! Clean import of the new PackButton component.

The import statement is well-placed and follows Vue.js conventions.


52-54: LGTM! Clean integration of the PackButton component.

The placement of the PackButton below the URL input field improves the user experience, as intended in the PR objectives. The props are correctly passed through to the component.

website/server/src/schemas/request.ts (2)

34-45: LGTM! Comprehensive file validation schema.

The schema includes essential validations:

  • File type check
  • ZIP format validation
  • Size limit (10MB)

61-66: LGTM! Well-designed request validation.

The refinements ensure:

  • At least one of URL or file is provided
  • Both URL and file cannot be provided simultaneously
website/server/src/index.ts (2)

48-51: LGTM! Clear error message for file size limit.

The error message has been improved from "overflow :(" to a more descriptive "File size too large :(".


77-85: LGTM! Clean conditional processing logic.

The code clearly handles both file and URL processing paths with proper error handling.

website/server/src/utils/fileUtils.ts (1)

8-53: LGTM! Robust ZIP extraction with security measures.

The implementation includes:

  • File size validation
  • Directory traversal prevention
  • Proper error handling
website/server/src/remoteRepo.ts (2)

30-32: LGTM! Clear URL validation.

The validation ensures the URL is present for remote processing.


34-35: LGTM! Updated cache key generation.

The cache key now includes the type parameter to differentiate between URL and file-based requests.

website/client/components/TryItFileUpload.vue (2)

16-31: Enhance file validation robustness.

The current validation could be improved to handle edge cases and provide better security.


90-174: Well-structured styling implementation!

The CSS implementation follows the design system, uses proper scoping, and implements responsive design patterns.

website/client/components/TryIt.vue (2)

102-117: Reset error states when switching modes.

When switching between URL and file upload modes, error states should be reset to prevent confusion.


145-180: Clean and consistent styling implementation!

The CSS implementation follows the design system and properly handles responsive design.

Comment on lines +64 to +76
export const cleanupTempDirectory = async (directory: string): Promise<void> => {
try {
if (!directory.includes('repomix-')) {
throw new AppError('Invalid temporary directory path');
}
await fs.rm(directory, { recursive: true, force: true });
} catch (error) {
if (error instanceof AppError) {
throw error;
}
console.error(`Failed to cleanup temporary directory: ${directory}`, error);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance temporary directory validation.

The current validation only checks for 'repomix-' in the path, which might be insufficient.

-    if (!directory.includes('repomix-')) {
+    const tempDirPath = path.resolve(directory);
+    const systemTempDir = path.resolve(os.tmpdir());
+    if (!tempDirPath.startsWith(systemTempDir) || !path.basename(tempDirPath).startsWith('repomix-')) {
       throw new AppError('Invalid temporary directory path');
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const cleanupTempDirectory = async (directory: string): Promise<void> => {
try {
if (!directory.includes('repomix-')) {
throw new AppError('Invalid temporary directory path');
}
await fs.rm(directory, { recursive: true, force: true });
} catch (error) {
if (error instanceof AppError) {
throw error;
}
console.error(`Failed to cleanup temporary directory: ${directory}`, error);
}
};
export const cleanupTempDirectory = async (directory: string): Promise<void> => {
try {
const tempDirPath = path.resolve(directory);
const systemTempDir = path.resolve(os.tmpdir());
if (!tempDirPath.startsWith(systemTempDir) || !path.basename(tempDirPath).startsWith('repomix-')) {
throw new AppError('Invalid temporary directory path');
}
await fs.rm(directory, { recursive: true, force: true });
} catch (error) {
if (error instanceof AppError) {
throw error;
}
console.error(`Failed to cleanup temporary directory: ${directory}`, error);
}
};

Comment on lines +17 to +39
export async function processZipFile(
file: File,
format: string,
options: PackOptions,
clientIp: string,
): Promise<PackResult> {
// Validate the request (excluding URL validation)
const validatedData = validateRequest(packRequestSchema, {
file,
format,
options,
});

// Rate limit check
if (!rateLimiter.isAllowed(clientIp)) {
const remainingTime = Math.ceil(rateLimiter.getRemainingTime(clientIp) / 1000);
throw new AppError(`Rate limit exceeded. Please try again in ${remainingTime} seconds.`, 429);
}

if (!file) {
throw new AppError('File is required for file processing', 400);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🛠️ Refactor suggestion

🧩 Analysis chain

Enhance security measures for file processing.

Consider adding additional security checks:

  1. Validate file mime type using a library like file-type
  2. Implement virus scanning for uploaded files
  3. Add a maximum number of files limit for zip extraction

🌐 Web query:

What are the best practices for secure file upload handling in Node.js?

💡 Result:

When handling file uploads in Node.js applications, implementing robust security measures is critical to prevent vulnerabilities like arbitrary code execution or server compromise. Below are essential practices supported by industry research:


Core Security Practices

1. Multi-Layered File Validation

  • Type Verification:

    • Whitelist allowed MIME types/extensions (e.g., image/jpeg, .png)
    • Use magic number checks to detect spoofed files[1][3][4]
    // Validate MIME type and extension
    const allowedTypes = ['image/jpeg', 'image/png'];
    if (!allowedTypes.includes(file.mimetype)) reject('Invalid type');
  • Size Restrictions:

    • Enforce server-side limits (e.g., maxFileSize: 5MB)[1][7]

2. Secure Filename Handling

  • Replace user-provided names with cryptographic hashes/UUIDs[1][4][10]
  • Prevent path traversal via sanitization:
    const safeName = `${crypto.randomUUID()}${path.extname(originalName)}`;

3. Storage Configuration

  • Store files outside web-accessible directories[1][3]
  • Use temporary storage during validation before permanent retention[4]
  • Set strict directory permissions (0o755)[1]

Advanced Protections

Technique Implementation
Antivirus Scanning Integrate APIs like Verisys for server-side malware detection[8]
HTTPS Enforcement Encrypt data in transit using TLS certificates[3][7]
Rate Limiting Prevent DDoS attacks via tools like express-rate-limit[1][7]
CSRF Protection Validate tokens using middleware like csurf[7]

Library Best Practices

  • Use battle-tested libraries like Formidable or Multer, but:
    1. Avoid trusting their default configurations[2][5]
    2. Combine multiple validation layers (extension + MIME + magic numbers)[1][3]
    3. Audit dependencies regularly[6][9]
// Formidable secure setup example
const form = formidable({
  maxFiles: 2,
  filter: ({ mimetype }) => ['image/jpeg'].includes(mimetype),
  filename: () => crypto.randomUUID()
});

Critical Industry Findings

A 2024 study of Node.js file upload libraries (Google Research[2][5]) revealed:

  • 19 CVEs identified in popular tools
  • Common failures in handling dynamic file types
  • Widespread lack of content validation beyond headers

By combining strict validation protocols, secure storage practices, and continuous dependency monitoring, developers can mitigate risks highlighted in recent research while adhering to modern security standards.

Citations:


Enhance security measures for file processing in processZipFile.

While the current implementation handles basic validations and rate limiting, further security enhancements are recommended. In particular, please consider:

  • File Type Verification:

    • Use a library such as file-type to perform magic number checks in addition to validating the file’s MIME type.
    • Whitelist specific MIME types and extensions to prevent spoofing.
  • Virus Scanning:

    • Integrate an antivirus scanning solution (or API) to inspect uploaded files for malware before processing.
  • Zip Extraction Limits:

    • Enforce a maximum number of files allowed during zip extraction to mitigate potential resource exhaustion or path traversal risks.

These additions will help to further harden the file upload process in accordance with current best practices for secure file handling in Node.js.

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