feat(04-04): add StdinSource, URLSource, and ClipboardSource
- StdinSource reads from an injectable io.Reader (INPUT-03) - URLSource fetches http/https with 30s timeout, 50MB cap, scheme whitelist, and Content-Type filter (INPUT-04) - ClipboardSource wraps atotto/clipboard with graceful fallback for missing tooling (INPUT-05) - emitByteChunks local helper mirrors file.go windowing to stay independent of sibling wave-1 plans - Tests cover happy path, cancellation, redirects, oversize bodies, binary content types, scheme rejection, and clipboard error paths
This commit is contained in:
45
pkg/engine/sources/clipboard.go
Normal file
45
pkg/engine/sources/clipboard.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package sources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/atotto/clipboard"
|
||||
|
||||
"github.com/salvacybersec/keyhunter/pkg/types"
|
||||
)
|
||||
|
||||
// ClipboardSource reads the current OS clipboard contents and emits them
|
||||
// as a single chunk stream with Source="clipboard". Requires xclip/xsel/
|
||||
// wl-clipboard on Linux, pbpaste on macOS, or native API on Windows.
|
||||
type ClipboardSource struct {
|
||||
// Reader overrides the clipboard reader; when nil the real clipboard is used.
|
||||
// Tests inject a func returning a fixture.
|
||||
Reader func() (string, error)
|
||||
ChunkSize int
|
||||
}
|
||||
|
||||
// NewClipboardSource returns a ClipboardSource bound to the real OS clipboard.
|
||||
func NewClipboardSource() *ClipboardSource {
|
||||
return &ClipboardSource{Reader: clipboard.ReadAll, ChunkSize: defaultChunkSize}
|
||||
}
|
||||
|
||||
// Chunks reads the clipboard and emits its contents.
|
||||
func (c *ClipboardSource) Chunks(ctx context.Context, out chan<- types.Chunk) error {
|
||||
if clipboard.Unsupported && c.Reader == nil {
|
||||
return errors.New("ClipboardSource: clipboard tooling unavailable (install xclip/xsel/wl-clipboard on Linux)")
|
||||
}
|
||||
reader := c.Reader
|
||||
if reader == nil {
|
||||
reader = clipboard.ReadAll
|
||||
}
|
||||
text, err := reader()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ClipboardSource: read: %w", err)
|
||||
}
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
return emitByteChunks(ctx, []byte(text), "clipboard", c.ChunkSize, out)
|
||||
}
|
||||
Reference in New Issue
Block a user