Add credential embedding in stream URLs for SSE events

Embed username:password@ credentials in discovered stream URLs for
basic_auth and combined authentication methods, making URLs directly
usable by clients without additional auth handling.

**Changes:**
- Add embedCredentialsInURL() function in scanner.go
- Embed credentials for basic_auth and combined methods only
- Skip embedding for no_auth, query_params, digest, url_embedded
- Handle edge cases: empty credentials, existing credentials in URL
- Apply in scanDirectStream() and testURLsConcurrently()

**Security:**
- URL encoding handled by url.UserPassword()
- Checks prevent credential duplication
- Only processes when username AND password are provided

**Results:**
Before: http://10.0.20.112/snapshot.jpg
After:  http://admin:password@10.0.20.112/snapshot.jpg

Tested with Zosi ZG23213M camera - all 6 streams working correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
eduard256
2025-10-29 02:31:58 +03:00
parent 453769a376
commit a5c769dd6c
+49 -2
View File
@@ -178,8 +178,12 @@ func (s *Scanner) scanDirectStream(ctx context.Context, req models.StreamDiscove
if testResult.Working {
result.TotalFound = 1
// Embed credentials in URL for basic_auth and combined methods
finalURL := s.embedCredentialsInURL(testResult.URL, req.Username, req.Password, string(testResult.AuthMethod))
discoveredStream := models.DiscoveredStream{
URL: testResult.URL,
URL: finalURL,
Type: testResult.Type,
Protocol: testResult.Protocol,
Working: true,
@@ -231,6 +235,46 @@ func (s *Scanner) extractIP(target string) string {
return target
}
// embedCredentialsInURL embeds username and password in URL for basic_auth and combined methods
func (s *Scanner) embedCredentialsInURL(streamURL, username, password, authMethod string) string {
// Only apply for basic_auth and combined methods
if authMethod != "basic_auth" && authMethod != "combined" {
return streamURL
}
// Check if credentials are provided
if username == "" || password == "" {
return streamURL
}
// Parse URL
u, err := url.Parse(streamURL)
if err != nil {
s.logger.Debug("failed to parse URL for credential embedding",
"url", streamURL,
"error", err.Error())
return streamURL
}
// Check if credentials already exist in URL
if u.User != nil {
s.logger.Debug("credentials already exist in URL, skipping embedding",
"url", streamURL)
return streamURL
}
// Embed credentials
u.User = url.UserPassword(username, password)
embeddedURL := u.String()
s.logger.Debug("credentials embedded in URL",
"original_url", streamURL,
"embedded_url", embeddedURL,
"auth_method", authMethod)
return embeddedURL
}
// collectURLs collects all URLs to test
func (s *Scanner) collectURLs(ctx context.Context, req models.StreamDiscoveryRequest, ip string) ([]string, error) {
var allURLs []string
@@ -444,8 +488,11 @@ func (s *Scanner) testURLsConcurrently(ctx context.Context, urls []string, req m
if testResult.Working {
atomic.AddInt32(&found, 1)
// Embed credentials in URL for basic_auth and combined methods
finalURL := s.embedCredentialsInURL(testResult.URL, req.Username, req.Password, string(testResult.AuthMethod))
discoveredStream := models.DiscoveredStream{
URL: testResult.URL,
URL: finalURL,
Type: testResult.Type,
Protocol: testResult.Protocol,
Port: 0, // Will be extracted from URL if needed