Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/large-payload-tester.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/nightly-mcp-stress-test.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ Available Commands:
Flags:
-c, --config string Path to config file
--config-stdin Read MCP server configuration from stdin (JSON format). When enabled, overrides --config
--enable-difc Enable DIFC enforcement and session requirement (requires sys___init call before tool access)
--enable-difc Enable DIFC enforcement for information flow control
--env string Path to .env file to load environment variables
-h, --help help for awmg
-l, --listen string HTTP server listen address (default "127.0.0.1:3000")
Expand Down
2 changes: 1 addition & 1 deletion config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ args = [
# ============================================================================

# Enable Data Information Flow Control (DIFC) security model (default: false)
# When true, requires sys___init call before tool access
# When enabled, enforces information flow control policies on tool calls
# This is an experimental feature - keep disabled for standard MCP compatibility
# enable_difc = false

Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/flags_difc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var (

func init() {
RegisterFlag(func(cmd *cobra.Command) {
cmd.Flags().BoolVar(&enableDIFC, "enable-difc", getDefaultEnableDIFC(), "Enable DIFC enforcement and session requirement (requires sys___init call before tool access)")
cmd.Flags().BoolVar(&enableDIFC, "enable-difc", getDefaultEnableDIFC(), "Enable DIFC enforcement for information flow control")
})
}

Expand Down
53 changes: 20 additions & 33 deletions internal/server/unified.go
Original file line number Diff line number Diff line change
Expand Up @@ -846,48 +846,35 @@ func (us *UnifiedServer) ensureSessionDirectory(sessionID string) error {
}

// requireSession checks that a session has been initialized for this request
// When DIFC is disabled (default), automatically creates a session if one doesn't exist
// Sessions are automatically created if one doesn't exist (for standard MCP client compatibility)
func (us *UnifiedServer) requireSession(ctx context.Context) error {
sessionID := us.getSessionID(ctx)
log.Printf("Checking session for ID: %s", sessionID)

// If DIFC is disabled (default), use double-checked locking to auto-create session
if !us.enableDIFC {
us.sessionMu.RLock()
session := us.sessions[sessionID]
us.sessionMu.RUnlock()

if session == nil {
// Need to create session - acquire write lock
us.sessionMu.Lock()
// Double-check after acquiring write lock to avoid race condition
if us.sessions[sessionID] == nil {
log.Printf("DIFC disabled: auto-creating session for ID: %s", sessionID)
us.sessions[sessionID] = NewSession(sessionID, "")
log.Printf("Session auto-created for ID: %s", sessionID)

// Ensure session directory exists in payload mount point
// This is done after releasing the lock to avoid holding it during I/O
us.sessionMu.Unlock()
if err := us.ensureSessionDirectory(sessionID); err != nil {
logger.LogWarn("client", "Failed to create session directory for session=%s: %v", sessionID, err)
// Don't fail - payloads will attempt to create the directory when needed
}
return nil
}
us.sessionMu.Unlock()
}
return nil
}

// DIFC is enabled - require explicit session initialization
// Use double-checked locking to auto-create session if needed
us.sessionMu.RLock()
session := us.sessions[sessionID]
us.sessionMu.RUnlock()

if session == nil {
log.Printf("Session not found for ID: %s. Available sessions: %v", sessionID, us.getSessionKeys())
return fmt.Errorf("sys___init must be called before any other tool calls")
// Need to create session - acquire write lock
us.sessionMu.Lock()
// Double-check after acquiring write lock to avoid race condition
if us.sessions[sessionID] == nil {
log.Printf("Auto-creating session for ID: %s", sessionID)
us.sessions[sessionID] = NewSession(sessionID, "")
log.Printf("Session auto-created for ID: %s", sessionID)

// Ensure session directory exists in payload mount point
// This is done after releasing the lock to avoid holding it during I/O
us.sessionMu.Unlock()
if err := us.ensureSessionDirectory(sessionID); err != nil {
logger.LogWarn("client", "Failed to create session directory for session=%s: %v", sessionID, err)
// Don't fail - payloads will attempt to create the directory when needed
}
return nil
Comment on lines 859 to +875
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

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

requireSession() now auto-creates sessions and calls ensureSessionDirectory(sessionID) based on the session ID from context. Since ensureSessionDirectory() uses filepath.Join(us.payloadDir, sessionID) without validating/sanitizing sessionID, a malicious or malformed session ID containing path separators, .., or an absolute path could cause directory creation outside the intended payload directory. Please validate the session ID before using it in filesystem paths (e.g., reject absolute paths, clean and ensure the result stays within us.payloadDir, and/or restrict to a safe character set).

Copilot uses AI. Check for mistakes.
Comment on lines 859 to +875
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

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

With sessions now always auto-created, any client that can influence the session ID can force unbounded growth of us.sessions and corresponding directories under the payload mount (disk usage / inode exhaustion). Consider adding a cap/TTL cleanup for us.sessions, or deferring directory creation until a payload actually needs to be written, to reduce DoS risk.

Copilot uses AI. Check for mistakes.
}
us.sessionMu.Unlock()
}

log.Printf("Session validated for ID: %s", sessionID)
Expand Down
19 changes: 13 additions & 6 deletions internal/server/unified_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,17 @@ func TestRequireSession(t *testing.T) {
err = us.requireSession(ctxWithSession)
assert.NoError(t, err, "requireSession() failed for valid session")

// Test with invalid session (DIFC enabled)
ctxWithInvalidSession := context.WithValue(ctx, SessionIDContextKey, "invalid-session")
err = us.requireSession(ctxWithInvalidSession)
require.Error(t, err, "requireSession() should fail for invalid session when DIFC is enabled")
// Test with non-existent session - should auto-create even with DIFC enabled
ctxWithNewSession := context.WithValue(ctx, SessionIDContextKey, "new-session")
err = us.requireSession(ctxWithNewSession)
require.NoError(t, err, "requireSession() should auto-create session even when DIFC is enabled")

// Verify session was created
us.sessionMu.RLock()
newSession, exists := us.sessions["new-session"]
us.sessionMu.RUnlock()
require.True(t, exists, "Session should have been auto-created")
require.NotNil(t, newSession, "Session should not be nil")
}

func TestRequireSession_DifcDisabled(t *testing.T) {
Expand Down Expand Up @@ -432,8 +439,8 @@ func TestRequireSession_EdgeCases(t *testing.T) {
enableDIFC: true,
sessionID: "nonexistent",
preCreate: false,
wantErr: true,
description: "should deny access to nonexistent session when DIFC enabled",
wantErr: false, // Sessions are auto-created regardless of DIFC setting
description: "should auto-create session even when DIFC enabled",
},
{
name: "DIFC disabled without session",
Expand Down
Loading