Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
7e92049
Bound filesystem reads, scans, and watch registrations
Raghav-Bansal-15 Sep 11, 2026
3d26055
Verify filesystem limits across desktop platforms
Raghav-Bansal-15 Sep 11, 2026
cc610e5
Exercise persistent Windows watch errors with an exclusive directory …
Raghav-Bansal-15 Sep 11, 2026
3ba3019
fix(filesystem): preserve Windows extended-path reads
kshivang Sep 13, 2026
9047ffa
fix(filesystem): preserve bounded reads through authorized file links
kshivang Sep 13, 2026
cc81ef6
ci: run full validation on security repair branches
Raghav-Bansal-15 Sep 14, 2026
c366fea
test(filesystem): separate watch delivery deadlines from Windows fixt…
Raghav-Bansal-15 Sep 14, 2026
3193f34
fix(filesystem): use ARM64 no-follow flag for bounded reads
kshivang Sep 15, 2026
c5e5e6f
test(filesystem): pin native leaf-link refusal
kshivang Sep 15, 2026
16fc013
Respect horizontal scrollers and commit trackpad navigation on release
shivanshu-risa Sep 14, 2026
26bd48e
Fix swipe cancellation, observer lifecycle and terminal delivery
shivanshu-risa Sep 14, 2026
9253ed5
Publish prior contact termination for home swipe attribution
shivanshu-risa Sep 14, 2026
d1bfeb8
fix(kernel): require verified caller identity for the run-configurati…
Antriksh1984 Sep 15, 2026
1f00daa
fix(secrets): make the secret listing RPCs fail soft on one bad row (…
Antriksh1984 Sep 15, 2026
e7446cd
fix(kernel): require verified caller identity for the project-data br…
Antriksh1984 Sep 15, 2026
8534e83
fix(git): make clone timeout cancellable (#660)
passionate-coder26 Sep 15, 2026
9abb478
fix(kernel): require verified caller identity for the split-view brid…
Antriksh1984 Sep 15, 2026
174ab45
feat(mcp): let operators set tool policy proactively (#636)
Antriksh1984 Sep 15, 2026
a206f69
feat(mcp): add a clickable activity log for recent tool calls (#662)
Antriksh1984 Sep 15, 2026
236937c
Fix/spotlight project session isolation (#593)
Ayush04-C Sep 15, 2026
d9c0cbb
fix(browser): keep virtual spreadsheet gestures page-owned
kshivang Sep 15, 2026
e1d495f
fix(browser): allow new outward gestures at Google Sheets edges
kshivang Sep 15, 2026
8999511
ui: refine MCP policy controls and dialog
kshivang Sep 15, 2026
54ab77f
ui: group MCP policies and use staged radio choices
kshivang Sep 15, 2026
de67e2a
ui: show searchable tool descriptions in MCP policies
kshivang Sep 15, 2026
8791305
ui: describe MCP tool policies on hover
kshivang Sep 15, 2026
9c78f50
feat: apply MCP tool policies by section and access mode
kshivang Sep 15, 2026
2d4e577
ui: add global MCP presets and friendly plugin section names
kshivang Sep 15, 2026
312eb1c
fix: address integration review of MCP policies and input handling
kshivang Sep 15, 2026
2a339a2
fix: restore CI after review follow-up changes
kshivang Sep 15, 2026
c4b4425
Merge remote-tracking branch 'origin/main' into dev
kshivang Sep 15, 2026
7082e2c
fix(run): make run-configuration disambiguation work on Windows (#653)
Vidhaankhare16 Sep 15, 2026
474f6c9
Gate JxBrowser auto-release on the compile-classpath modules (#678)
Rushikeshiitb Sep 15, 2026
5f15d98
feat(cli): report workspace health in boss status and add boss doctor…
Vidhaankhare16 Sep 15, 2026
341010c
fix(cli): write piped CLI output as UTF-8 (#604)
Vidhaankhare16 Sep 15, 2026
283b450
feat(cli): add plugin init, validate, and link commands with dev hot-…
Kanakk23 Sep 15, 2026
b8b0900
feat(import): Bitwarden JSON and KeePass XML password importers (#679)
Rushikeshiitb Sep 15, 2026
1e1c9d3
fix(filesystem): report failed file mutations (#680)
Ayush04-C Sep 15, 2026
a448083
fix(updater): stop the update prompt stacking over another modal (#69…
Rushikeshiitb Sep 15, 2026
4bfd81d
Merge current dev while preserving reviewed ARM64 filesystem fixes
Raghav-Bansal-15 Sep 15, 2026
4b83b6b
fix(run): make run-configuration disambiguation work on Windows (#653)
Vidhaankhare16 Sep 15, 2026
35dfe48
Gate JxBrowser auto-release on the compile-classpath modules (#678)
Rushikeshiitb Sep 15, 2026
69a6b76
feat(cli): report workspace health in boss status and add boss doctor…
Vidhaankhare16 Sep 15, 2026
aa03486
fix(cli): write piped CLI output as UTF-8 (#604)
Vidhaankhare16 Sep 15, 2026
0e3a7d1
feat(cli): add plugin init, validate, and link commands with dev hot-…
Kanakk23 Sep 15, 2026
abb8686
feat(import): Bitwarden JSON and KeePass XML password importers (#679)
Rushikeshiitb Sep 15, 2026
65e83df
fix(filesystem): report failed file mutations (#680)
Ayush04-C Sep 15, 2026
35e80f9
fix(updater): stop the update prompt stacking over another modal (#69…
Rushikeshiitb Sep 15, 2026
77c4a40
fix(plugins): keep a plugin's app-wide registrations when another win…
nikhil-jangir-iitk Sep 15, 2026
40164e6
Merge current dev while retaining bounded filesystem operations
kshivang Sep 15, 2026
5f458b2
test(filesystem): cover Linux ARM64 and report RPC failures
kshivang Sep 15, 2026
8409e80
fix: validate resolved directory scan roots
Raghav-Bansal-15 Sep 15, 2026
6123d2d
Merge current dev preserving reviewed filesystem limits
kshivang Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/filesystem-limits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Filesystem resource limits

on:
push:
branches: ['fix/security-filesystem-limits']
pull_request:
paths:
- 'modules/boss-service-filesystem/**'
- 'modules/boss-ipc/src/main/proto/boss/ipc/v1/services/filesystem.proto'
- 'plugin-platform/plugin-api-ipc/**'
- '.github/workflows/filesystem-limits.yml'
workflow_dispatch:

permissions:
contents: read

jobs:
filesystem:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-java@v6.0.0
with:
distribution: temurin
java-version: '17'
- uses: gradle/actions/setup-gradle@v6
- name: Run filesystem and paging regressions
shell: bash
run: ./gradlew :boss-service-filesystem:test :plugin-platform:plugin-api-ipc:desktopTest :boss-service-filesystem:ktlintCheck :boss-service-filesystem:detekt :plugin-platform:plugin-api-ipc:ktlintCheck :plugin-platform:plugin-api-ipc:detekt --no-daemon
- uses: actions/upload-artifact@v7
if: always()
with:
name: filesystem-limits-${{ matrix.os }}
path: |
modules/boss-service-filesystem/build/test-results
plugin-platform/plugin-api-ipc/build/test-results
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ message ScanDirectoryRequest {
bool recursive = 2; // If true, scan subdirectories too
bool include_hidden = 3; // If true, include hidden files/dirs (starting with '.')
repeated string extensions = 4; // Filter by extension (e.g., "kt", "ts"). Empty = all.
int32 max_depth = 5; // Max recursion depth (0 = no limit)
int32 max_depth = 5; // Requested depth; server caps depth at 64 and scanned entries at 10,000
}

message ScanDirectoryResponse {
Expand All @@ -64,7 +64,7 @@ message ScanDirectoryResponse {
message ReadFileRequest {
string path = 1;
int64 offset_bytes = 2; // 0 = start
int64 max_bytes = 3; // 0 = read all
int64 max_bytes = 3; // Explicit page size, capped at 1 MiB; 0 = read all or refuse an oversized file
}

message ReadFileResponse {
Expand Down
1 change: 1 addition & 0 deletions modules/boss-service-filesystem/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {
implementation(project(":boss-ipc"))
implementation(libs.kotlinx.coroutines.core)
implementation(libs.slf4j.api)
implementation(libs.jna)
runtimeOnly(libs.slf4j.simple)

testImplementation(libs.kotlin.test.junit)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package ai.rever.boss.service.filesystem

import ai.rever.boss.ipc.proto.services.FileEntry
import ai.rever.boss.ipc.proto.services.ScanDirectoryRequest
import kotlinx.coroutines.ensureActive
import java.nio.file.FileVisitResult
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.SimpleFileVisitor
import java.nio.file.attribute.BasicFileAttributes
import kotlin.coroutines.CoroutineContext

internal class BoundedDirectoryScan(
private val request: ScanDirectoryRequest,
private val context: CoroutineContext,
private val validateTarget: (String) -> Unit,
) {
private val requestedDepth = if (!request.recursive) 1 else request.maxDepth.takeIf { it > 0 } ?: Int.MAX_VALUE
private val depth = requestedDepth.coerceAtMost(FileSystemLimits.SCAN_DEPTH)
private val extensions = request.extensionsList.toSet()
private val entries = mutableListOf<FileEntry>()
private var visited = 0
private var responseBytes = 0

fun scan(root: Path): List<FileEntry> {
val target = root.toRealPath()
validateTarget(target.toString())
Files.walkFileTree(
target,
emptySet(),
depth,
object : SimpleFileVisitor<Path>() {
override fun preVisitDirectory(
dir: Path,
attrs: BasicFileAttributes,
) = visit(target, root, dir, attrs)

override fun visitFile(
file: Path,
attrs: BasicFileAttributes,
) = visit(target, root, file, attrs)
},
)
return entries
}

private fun visit(
root: Path,
visibleRoot: Path,
path: Path,
attributes: BasicFileAttributes,
): FileVisitResult {
context.ensureActive()
enforceFileSystemLimit(++visited <= FileSystemLimits.SCAN_ENTRIES, "Directory scan entry limit reached")
val name = path.fileName?.toString().orEmpty()
return when {
path == root -> {
FileVisitResult.CONTINUE
}

!request.includeHidden && name.startsWith('.') -> {
FileVisitResult.SKIP_SUBTREE
}

else -> {
checkDepth(root, path, attributes)
if (attributes.isDirectory || extensions.isEmpty() || name.substringAfterLast('.', "") in extensions) {
add(visibleRoot.resolve(root.relativize(path)), attributes)
}
FileVisitResult.CONTINUE
}
}
}

private fun checkDepth(
root: Path,
path: Path,
attributes: BasicFileAttributes,
) {
if (attributes.isDirectory && requestedDepth > depth && root.relativize(path).nameCount == depth) {
Files.newDirectoryStream(path).use {
enforceFileSystemLimit(!it.iterator().hasNext(), "Directory scan depth limit reached")
}
}
}

private fun add(
path: Path,
attributes: BasicFileAttributes,
) {
val entry =
FileEntry
.newBuilder()
.setPath(path.toString())
.setName(path.fileName.toString())
.setIsDirectory(attributes.isDirectory)
.setSizeBytes(if (attributes.isDirectory) 0L else attributes.size())
.setModifiedAt(attributes.lastModifiedTime().toMillis())
.setIsHidden(path.fileName.toString().startsWith('.'))
.build()
responseBytes += entry.serializedSize + 8
enforceFileSystemLimit(responseBytes <= FileSystemLimits.SCAN_BYTES, "Directory scan response limit reached")
entries.add(entry)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ai.rever.boss.service.filesystem

import io.grpc.Status

/** Bounded RPC pages fit below gRPC's default message limit; callers can page large file reads explicitly. */
internal object FileSystemLimits {
const val READ_BYTES = 1_048_576
const val SCAN_ENTRIES = 10_000
const val SCAN_DEPTH = 64
const val SCAN_BYTES = 2_097_152
}

internal fun fileSystemLimit(message: String) = Status.RESOURCE_EXHAUSTED.withDescription(message).asRuntimeException()

internal fun enforceFileSystemLimit(
condition: Boolean,
message: String,
) {
if (!condition) throw fileSystemLimit(message)
}
Loading