-
Notifications
You must be signed in to change notification settings - Fork 15
Add Ollama Local LLM Working Mode #38
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
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: elbruno <[email protected]>
|
|
||
| _logger.LogInformation( | ||
| "Starting {OrchestrationTypeName} orchestration for query: {ProductQuery} using MAF Ollama Agents", | ||
| request.Orchestration, request.ProductQuery); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
To fix the problem, we should ensure that any user-provided value included in log messages is sanitized before being logged. For plain-text logs, the primary concern is control characters like \r and \n which can create additional lines or otherwise break log framing; removing or normalizing them is sufficient and preserves most of the value of the log message.
The best minimally invasive fix here is to create a sanitized version of request.ProductQuery right before logging, by removing carriage returns and newlines (and optionally other line separators) and using that sanitized value in the log call. We keep the logging message and functionality the same, just with safer input. No changes are needed to method signatures or other functionality.
Concretely, in AssistAsync within src/MultiAgentDemo/Controllers/MultiAgentControllerMAFOllama.cs, immediately before the _logger.LogInformation call, create a local variable, e.g., sanitizedProductQuery, which takes request.ProductQuery and calls Replace(Environment.NewLine, string.Empty) and also removes \n and \r explicitly (to cover all typical cases). Then, update the _logger.LogInformation call to pass sanitizedProductQuery instead of request.ProductQuery. No new imports are needed, since Environment is in System, which is implicitly available.
-
Copy modified lines R56-R60 -
Copy modified line R63
| @@ -53,9 +53,14 @@ | ||
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| var sanitizedProductQuery = request.ProductQuery | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
|
|
||
| _logger.LogInformation( | ||
| "Starting {OrchestrationTypeName} orchestration for query: {ProductQuery} using MAF Ollama Agents", | ||
| request.Orchestration, request.ProductQuery); | ||
| request.Orchestration, sanitizedProductQuery); | ||
|
|
||
| try | ||
| { |
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| _logger.LogInformation("Starting sequential workflow with Ollama agents for query: {ProductQuery}", request.ProductQuery); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
This log entry depends on a
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
To fix the problem, we should sanitize user-provided strings before logging them, ensuring they cannot inject line breaks or other control characters into log files. For plain-text logs, a common pattern is to replace \r and \n with spaces or remove them entirely before passing the value into the logger.
The best way to fix this without changing existing functionality is to introduce a small, private helper method in MultiAgentControllerMAFOllama that takes a string and returns a sanitized version with line breaks removed (or replaced), and then use that helper when logging request.ProductQuery. We’ll keep the log message and template unchanged and only adjust the value passed for {ProductQuery}. Specifically:
- In
src/MultiAgentDemo/Controllers/MultiAgentControllerMAFOllama.cs, inside theMultiAgentControllerMAFOllamaclass, add a private method, e.g.SanitizeForLogging(string? value), that:- Returns the input if it is
null. - Replaces
\rand\nwith spaces (or removes them).
- Returns the input if it is
- Update the log statement in
AssistSequentialAsyncat line 90 to callSanitizeForLogging(request.ProductQuery)instead of loggingrequest.ProductQuerydirectly. - Optionally, we could also use this helper in the other log line that includes
request.ProductQuery(line 56–58) to comprehensively address similar issues, but per the description, CodeQL’s highlighted variant is line 90; we will still instrument line 56 as it’s the same pattern and within the shown snippet.
No new external dependencies are required; we can implement this with basic string.Replace.
-
Copy modified lines R28-R44 -
Copy modified line R75 -
Copy modified lines R107-R109
| @@ -25,6 +25,23 @@ | ||
| private readonly Workflow _sequentialWorkflow; | ||
| private readonly Workflow _concurrentWorkflow; | ||
|
|
||
| /// <summary> | ||
| /// Sanitizes user-provided strings for safe logging by removing line breaks. | ||
| /// </summary> | ||
| /// <param name="value">The potentially unsafe string value.</param> | ||
| /// <returns>A sanitized string safe for inclusion in log entries.</returns> | ||
| private static string? SanitizeForLogging(string? value) | ||
| { | ||
| if (string.IsNullOrEmpty(value)) | ||
| { | ||
| return value; | ||
| } | ||
|
|
||
| return value | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
| } | ||
|
|
||
| public MultiAgentControllerMAFOllama( | ||
| ILogger<MultiAgentControllerMAFOllama> logger, | ||
| MAFOllamaAgentProvider ollamaAgentProvider) | ||
| @@ -55,7 +72,7 @@ | ||
|
|
||
| _logger.LogInformation( | ||
| "Starting {OrchestrationTypeName} orchestration for query: {ProductQuery} using MAF Ollama Agents", | ||
| request.Orchestration, request.ProductQuery); | ||
| request.Orchestration, SanitizeForLogging(request.ProductQuery)); | ||
|
|
||
| try | ||
| { | ||
| @@ -87,7 +104,9 @@ | ||
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| _logger.LogInformation("Starting sequential workflow with Ollama agents for query: {ProductQuery}", request.ProductQuery); | ||
| _logger.LogInformation( | ||
| "Starting sequential workflow with Ollama agents for query: {ProductQuery}", | ||
| SanitizeForLogging(request.ProductQuery)); | ||
|
|
||
| try | ||
| { |
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| _logger.LogInformation("Starting concurrent workflow with Ollama agents for query: {ProductQuery}", request.ProductQuery); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
This log entry depends on a
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
General approach: Sanitize user input before logging so that it cannot inject extra log entries. For plain-text-oriented logs, removing newline characters (\r, \n) and possibly other control characters is sufficient and preserves most of the content. We should apply this consistently to all log statements that include request.ProductQuery.
Best fix here: In this file, introduce a small helper that removes newline characters from a string and use it to sanitize request.ProductQuery before passing it to _logger.LogInformation. We will not alter the log template or other behavior, only the value passed for {ProductQuery}. Specifically:
- Add a private helper method in
MultiAgentControllerMAFOllamasuch as:
private static string SanitizeForLogging(string? input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return input
.Replace("\r\n", " ")
.Replace("\n", " ")
.Replace("\r", " ");
}(Using spaces keeps the query readable while preventing multi-line logs.)
- In
AssistAsync, change theLogInformationcall at lines 56–58 to passSanitizeForLogging(request.ProductQuery)instead ofrequest.ProductQuery. - In
AssistSequentialAsync, change theLogInformationcall at line 90 similarly. - In
AssistConcurrentAsync, change theLogInformationcall at line 115 similarly (this is the exact sink CodeQL flagged).
No new imports are needed; we use only string operations already available.
-
Copy modified lines R28-R40 -
Copy modified line R71 -
Copy modified line R103 -
Copy modified line R128
| @@ -25,6 +25,19 @@ | ||
| private readonly Workflow _sequentialWorkflow; | ||
| private readonly Workflow _concurrentWorkflow; | ||
|
|
||
| private static string SanitizeForLogging(string? input) | ||
| { | ||
| if (string.IsNullOrEmpty(input)) | ||
| { | ||
| return string.Empty; | ||
| } | ||
|
|
||
| return input | ||
| .Replace("\r\n", " ") | ||
| .Replace("\n", " ") | ||
| .Replace("\r", " "); | ||
| } | ||
|
|
||
| public MultiAgentControllerMAFOllama( | ||
| ILogger<MultiAgentControllerMAFOllama> logger, | ||
| MAFOllamaAgentProvider ollamaAgentProvider) | ||
| @@ -55,7 +68,7 @@ | ||
|
|
||
| _logger.LogInformation( | ||
| "Starting {OrchestrationTypeName} orchestration for query: {ProductQuery} using MAF Ollama Agents", | ||
| request.Orchestration, request.ProductQuery); | ||
| request.Orchestration, SanitizeForLogging(request.ProductQuery)); | ||
|
|
||
| try | ||
| { | ||
| @@ -87,7 +100,7 @@ | ||
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| _logger.LogInformation("Starting sequential workflow with Ollama agents for query: {ProductQuery}", request.ProductQuery); | ||
| _logger.LogInformation("Starting sequential workflow with Ollama agents for query: {ProductQuery}", SanitizeForLogging(request.ProductQuery)); | ||
|
|
||
| try | ||
| { | ||
| @@ -112,7 +125,7 @@ | ||
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| _logger.LogInformation("Starting concurrent workflow with Ollama agents for query: {ProductQuery}", request.ProductQuery); | ||
| _logger.LogInformation("Starting concurrent workflow with Ollama agents for query: {ProductQuery}", SanitizeForLogging(request.ProductQuery)); | ||
|
|
||
| try | ||
| { |
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| _logger.LogInformation("Starting handoff workflow with Ollama agents for query: {ProductQuery}", request.ProductQuery); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
This log entry depends on a
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to fix log forging issues, user-provided strings must be normalized/encoded before being written to logs. For plain text logs, this usually means removing or replacing newline (\n, \r) and similar control characters so a single logical log entry cannot be split into multiple lines by crafted input. For HTML- or UI-displayed logs, HTML or other appropriate encoding should be applied.
The best fix here, without changing existing behavior, is to sanitize request.ProductQuery before passing it to _logger.LogInformation. We can do this inline in the logging call by replacing occurrences of Environment.NewLine, \n, and \r with safe alternatives such as a space or empty string. This keeps the content semantics mostly intact while preventing multi-line log injection. We will apply this consistently to all logging statements that include request.ProductQuery in this controller: the generic AssistAsync method and the specific workflows, including the highlighted handoff workflow at line 140 and the concurrent workflow at line 115. These are all in src/MultiAgentDemo/Controllers/MultiAgentControllerMAFOllama.cs, and we can implement the fix without adding new imports by using string.Replace, which is part of System.String.
Concretely:
- In
AssistAsync, change theLogInformationcall at lines 56–58 to use a sanitized version ofrequest.ProductQuery. - In
AssistConcurrentAsync(the concurrent workflow), change theLogInformationcall at line 115 similarly. - In
AssistHandoffAsync(the handoff workflow), change theLogInformationcall at line 140 similarly.
To avoid duplicating replacement logic in many places without adding new methods the snippet doesn’t show, we will apply a small inline sanitization expression (chainedReplacecalls) directly in each logging call.
-
Copy modified lines R58-R62 -
Copy modified lines R119-R124 -
Copy modified lines R149-R154
| @@ -55,7 +55,11 @@ | ||
|
|
||
| _logger.LogInformation( | ||
| "Starting {OrchestrationTypeName} orchestration for query: {ProductQuery} using MAF Ollama Agents", | ||
| request.Orchestration, request.ProductQuery); | ||
| request.Orchestration, | ||
| request.ProductQuery? | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty)); | ||
|
|
||
| try | ||
| { | ||
| @@ -112,7 +116,12 @@ | ||
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| _logger.LogInformation("Starting concurrent workflow with Ollama agents for query: {ProductQuery}", request.ProductQuery); | ||
| _logger.LogInformation( | ||
| "Starting concurrent workflow with Ollama agents for query: {ProductQuery}", | ||
| request.ProductQuery? | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty)); | ||
|
|
||
| try | ||
| { | ||
| @@ -137,7 +146,12 @@ | ||
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| _logger.LogInformation("Starting handoff workflow with Ollama agents for query: {ProductQuery}", request.ProductQuery); | ||
| _logger.LogInformation( | ||
| "Starting handoff workflow with Ollama agents for query: {ProductQuery}", | ||
| request.ProductQuery? | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty)); | ||
|
|
||
| try | ||
| { |
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| _logger.LogInformation("Starting group chat workflow with Ollama agents for query: {ProductQuery}", request.ProductQuery); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
This log entry depends on a
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to fix log-forging risks, user-controlled strings must be normalized before being written to logs, typically by removing or replacing newline and other control characters so that a single log event cannot visually or structurally masquerade as multiple log entries. For plain-text logs, a common minimal fix is to strip \r and \n from user input before logging; for HTML logs, HTML encoding is recommended instead.
In this specific file, the vulnerable use is in AssistGroupChatAsync, where request.ProductQuery is logged directly. We should ensure that the value passed to the logger has newline characters removed. To keep behavior unchanged aside from safety, we can compute a sanitized local variable just before logging, using Replace to strip \r and \n, and then log that sanitized value. To address any similar future issues consistently, we can also add a small private helper method within this controller to sanitize log values, and use it at the logging call site; this does not change semantics other than removing control characters. The edits are limited to src/MultiAgentDemo/Controllers/MultiAgentControllerMAFOllama.cs around the AssistGroupChatAsync method: introduce a private SanitizeForLogging method near the bottom of the class, and modify the _logger.LogInformation line to call this method on request.ProductQuery.
-
Copy modified lines R171-R172 -
Copy modified lines R324-R335
| @@ -168,7 +168,8 @@ | ||
| return BadRequest("Request body is required and must include a ProductQuery."); | ||
| } | ||
|
|
||
| _logger.LogInformation("Starting group chat workflow with Ollama agents for query: {ProductQuery}", request.ProductQuery); | ||
| var sanitizedProductQuery = SanitizeForLogging(request.ProductQuery); | ||
| _logger.LogInformation("Starting group chat workflow with Ollama agents for query: {ProductQuery}", sanitizedProductQuery); | ||
|
|
||
| try | ||
| { | ||
| @@ -320,4 +321,16 @@ | ||
| _ => | ||
| "Multi-agent workflow using MAF Ollama Agents (llama3.2)." | ||
| }; | ||
| private static string SanitizeForLogging(string? value) | ||
| { | ||
| if (string.IsNullOrEmpty(value)) | ||
| { | ||
| return string.Empty; | ||
| } | ||
|
|
||
| // Remove newline characters to mitigate log forging in plain-text logs | ||
| return value.Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
| } | ||
|
|
||
| } |
| [HttpPost("assist/magentic")] | ||
| public async Task<ActionResult<MultiAgentResponse>> AssistMagenticAsync([FromBody] MultiAgentRequest? request) | ||
| { | ||
| _logger.LogInformation("MagenticOne workflow requested for query: {ProductQuery}", request?.ProductQuery); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
This log entry depends on a
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to avoid log forging, user-provided strings should be sanitized before being written to logs. For plain-text style logs, the minimal and common mitigation is to remove newline and carriage-return characters from user input (and potentially other control characters) so an attacker cannot break the log line or visually inject additional entries.
For this specific issue in MultiAgentControllerMAFOllama.AssistMagenticAsync, we should avoid passing request?.ProductQuery directly to the logger. Instead, we can construct a sanitized version that replaces Environment.NewLine, \n, and \r with spaces (or removes them), and then pass that sanitized value as the ProductQuery property in the log call. This keeps the log message and structure identical from the perspective of the rest of the system while ensuring that any malicious line breaks in the query do not affect the integrity of the logs.
Concretely:
- In
AssistMagenticAsync, introduce a local variable, e.g.sanitizedProductQuery, that is assigned fromrequest?.ProductQuerywith line breaks removed or normalized. - Use this sanitized variable in the
_logger.LogInformationcall instead ofrequest?.ProductQuery. - No new imports are needed; we can rely on
string.ReplaceandEnvironment.NewLinefrom the standard library.
Only lines around 202–205 in src/MultiAgentDemo/Controllers/MultiAgentControllerMAFOllama.cs need modification.
-
Copy modified lines R204-R208 -
Copy modified lines R210-R211
| @@ -201,8 +201,14 @@ | ||
| [HttpPost("assist/magentic")] | ||
| public async Task<ActionResult<MultiAgentResponse>> AssistMagenticAsync([FromBody] MultiAgentRequest? request) | ||
| { | ||
| _logger.LogInformation("MagenticOne workflow requested for query: {ProductQuery}", request?.ProductQuery); | ||
| var productQuery = request?.ProductQuery ?? string.Empty; | ||
| var sanitizedProductQuery = productQuery | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty); | ||
|
|
||
| _logger.LogInformation("MagenticOne workflow requested for query: {ProductQuery}", sanitizedProductQuery); | ||
|
|
||
| return StatusCode(501, | ||
| "The MagenticOne workflow is not yet implemented in the MAF Ollama framework. " + | ||
| "Please use another orchestration type."); |
| { | ||
| _logger.LogInformation( | ||
| "Starting analysis for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", | ||
| customerId); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 4 hours ago
To fix the problem, any user-controlled value written to logs should be sanitized to prevent log forging. For plain-text logs, this typically means removing or normalizing newline and other control characters from the value before logging, while still logging sufficient information for diagnostics. We should also avoid modifying the log message template to keep existing functionality and structured logging behavior intact.
The best fix here is to sanitize customerId just before it is passed to _logger.LogInformation. We can create a local variable, e.g., sanitizedCustomerId, that removes newline characters from the original customerId using Replace (and optionally other control characters if desired), and then use that sanitized value in the log call. This keeps the logging statement’s template and structure the same, but ensures that any newline characters supplied by the user cannot break the log format. The change is localized to the AnalyzeAsync method in src/SingleAgentDemo/Controllers/SingleAgentControllerMAFOllama.cs. No new imports are required because string.Replace is available by default.
Concretely:
- In
AnalyzeAsync, right before the_logger.LogInformationcall, introducevar sanitizedCustomerId = customerId?.Replace(Environment.NewLine, string.Empty).Replace("\n", string.Empty).Replace("\r", string.Empty);(or similar). - Change the
_logger.LogInformationcall to usesanitizedCustomerIdinstead ofcustomerId. - Ensure we only alter those lines and keep all other behavior unchanged.
-
Copy modified lines R104-R108 -
Copy modified line R111
| @@ -101,9 +101,14 @@ | ||
| { | ||
| try | ||
| { | ||
| var sanitizedCustomerId = customerId? | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty); | ||
|
|
||
| _logger.LogInformation( | ||
| "Starting analysis for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", | ||
| customerId); | ||
| sanitizedCustomerId); | ||
|
|
||
| // Store image data in a temporary field for tool access | ||
| _currentImage = image; |
| // Build response from collected tool outputs | ||
| var analysisResponse = BuildResponseFromToolOutputs(result, prompt); | ||
|
|
||
| _logger.LogInformation("Analysis complete for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", customerId); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 4 hours ago
To fix this, we should sanitize the user-provided customerId before including it in log entries. The general approach is to normalize/encode user input so that it cannot inject line breaks or other control characters into the log stream. For plain-text logs, removing \r and \n from user input (and optionally trimming) is sufficient to prevent straightforward log forging via newlines.
The best minimal fix here is to introduce a sanitized local variable (e.g., safeCustomerId) inside the AnalyzeAsync method, derived from customerId with line breaks removed, and then use this sanitized value in all log messages in this method (both success and error paths). This avoids changing the method’s external behavior while ensuring log safety. We don’t need new imports or helper methods; we can use Replace("\r", "").Replace("\n", "") directly. Concretely:
- Right after entering the
tryblock (or just after parameters are available), createvar safeCustomerId = customerId?.Replace("\r", string.Empty).Replace("\n", string.Empty);. - Replace
customerIdwithsafeCustomerIdin the log calls at lines 104–106, 140–141, and 146–147. - Optionally keep using the original
customerIdfor business logic and responses, since the risk identified is specifically about logging.
-
Copy modified lines R104-R107 -
Copy modified line R110 -
Copy modified line R142 -
Copy modified line R148
| @@ -101,9 +101,13 @@ | ||
| { | ||
| try | ||
| { | ||
| var safeCustomerId = customerId? | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
|
|
||
| _logger.LogInformation( | ||
| "Starting analysis for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", | ||
| customerId); | ||
| safeCustomerId); | ||
|
|
||
| // Store image data in a temporary field for tool access | ||
| _currentImage = image; | ||
| @@ -137,13 +139,13 @@ | ||
| // Build response from collected tool outputs | ||
| var analysisResponse = BuildResponseFromToolOutputs(result, prompt); | ||
|
|
||
| _logger.LogInformation("Analysis complete for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", customerId); | ||
| _logger.LogInformation("Analysis complete for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", safeCustomerId); | ||
|
|
||
| return Ok(analysisResponse); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "Error in analysis for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", customerId); | ||
| _logger.LogError(ex, "Error in analysis for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", safeCustomerId); | ||
|
|
||
| // Return fallback response on error | ||
| return Ok(BuildFallbackResponse(prompt, customerId)); |
| } | ||
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "Error in analysis for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", customerId); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 4 hours ago
To fix the issue, user-provided values used in log messages should be sanitized before being passed to the logger. For plain-text logs, the key risk is newline and carriage-return characters that can split or visually corrupt log entries. A minimal, non-breaking mitigation is to normalize customerId into a version where \r and \n are removed (or replaced) before logging, while still using the original value for application behavior.
The best way to fix this snippet without changing external behavior is:
- Introduce a local variable, e.g.,
sanitizedCustomerId, which is derived fromcustomerIdby removing newline and carriage-return characters usingReplace. - Use
sanitizedCustomerIdinstead ofcustomerIdin all logging calls withinAnalyzeAsync. - Keep using the original
customerIdfor all non-logging behavior (e.g., passing toBuildFallbackResponse), so functionality remains unchanged.
Concretely, in AnalyzeAsync in src/SingleAgentDemo/Controllers/SingleAgentControllerMAFOllama.cs:
- Immediately after entering the method (or right before the first log call), define:
var sanitizedCustomerId = customerId? .Replace("\r", string.Empty) .Replace("\n", string.Empty);
- Replace the use of
customerIdin the_logger.LogInformationand_logger.LogErrorcalls withsanitizedCustomerId. No new imports are required becausestring.Replaceis inSystem, which is already available.
-
Copy modified lines R102-R105 -
Copy modified line R110 -
Copy modified line R140 -
Copy modified line R146
| @@ -99,11 +99,15 @@ | ||
| [FromForm] string prompt, | ||
| [FromForm] string customerId) | ||
| { | ||
| var sanitizedCustomerId = customerId? | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
|
|
||
| try | ||
| { | ||
| _logger.LogInformation( | ||
| "Starting analysis for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", | ||
| customerId); | ||
| sanitizedCustomerId); | ||
|
|
||
| // Store image data in a temporary field for tool access | ||
| _currentImage = image; | ||
| @@ -137,13 +137,13 @@ | ||
| // Build response from collected tool outputs | ||
| var analysisResponse = BuildResponseFromToolOutputs(result, prompt); | ||
|
|
||
| _logger.LogInformation("Analysis complete for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", customerId); | ||
| _logger.LogInformation("Analysis complete for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", sanitizedCustomerId); | ||
|
|
||
| return Ok(analysisResponse); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "Error in analysis for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", customerId); | ||
| _logger.LogError(ex, "Error in analysis for customer {CustomerId} using Single Agent with MCP Tools (Ollama)", sanitizedCustomerId); | ||
|
|
||
| // Return fallback response on error | ||
| return Ok(BuildFallbackResponse(prompt, customerId)); |
Co-authored-by: elbruno <[email protected]>
Switched all references from "ministral-3" to "llama3.2" as the default Ollama chat model. Updated orchestration descriptions, working mode documentation, and configuration defaults. Added explicit support for the MafOllama framework in AnalyzePhotoService to ensure correct endpoint usage. These changes ensure consistency and reflect the adoption of "llama3.2" for all MAF Ollama agent workflows.
- Added ZavaMAFOllama project references to all relevant services
- Registered and injected MAFOllama agent providers in DI and controllers
- Introduced new /maf_ollama endpoints for all agent-based APIs
- Standardized all API routes to use analyze_{operation}/{framework} naming
- Updated service classes to use new endpoint conventions and support MafOllama
- Ensured all agent framework switches include MafOllama
- Improves extensibility and consistency across agent framework APIs
Integrate Ollama-based agents and embedding generators into all major services. Controllers now support both local and Ollama agents, with new `/llm` endpoints using Ollama. MemoryContext and agent provider registration updated for dynamic agent selection. Workflows and configuration extended for Ollama. Enables flexible LLM-backed reasoning and search.
AISearch now respects the workingMode parameter provided by the caller, defaulting to MafLocal if not specified, instead of always using MafOllama. This change allows for more flexible and configurable AI search behavior.
| return BadRequest("No image file was provided."); | ||
| } | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Analyzing photo. Prompt: {{Prompt}}", prompt); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to fix log forging issues, sanitize or encode any user-provided data before writing it to logs. For plain-text style logs, removing newline and carriage-return characters (and optionally other control characters) is typically sufficient. For logs that might be rendered as HTML, HTML-encoding user input before logging is recommended.
In this file, the risky behavior is logging the raw prompt in the AnalyzeMAFOllamaAsync method (and, for consistency and safety, the other similar methods). The best fix with minimal functional impact is to create a sanitized version of prompt that strips newline characters and log that instead, while continuing to use the original prompt for actual analysis logic so behavior doesn’t change. We can do this inline by introducing a new local variable, e.g., var sanitizedPromptForLogging = prompt?.Replace("\r", "").Replace("\n", " "); right before each log call, and then pass sanitizedPromptForLogging to _logger.LogInformation. This requires no new imports and keeps logging semantics nearly identical, except that line breaks and carriage returns from the user will no longer appear in log output.
Specifically:
- In
AnalyzeLLMAsync,AnalyzeMAFLocalAsync,AnalyzeMAFFoundryAsync, andAnalyzeMAFOllamaAsync, define a sanitized variable immediately before_logger.LogInformation(...). - Replace the log argument
promptwith that sanitized variable. - Do not alter
promptitself, or its subsequent use inAnalyzeWithAgentAsync.
All changes will be made in src/AnalyzePhotoService/Controllers/PhotoAnalysisController.cs within the shown methods.
-
Copy modified lines R37-R39 -
Copy modified lines R41-R42 -
Copy modified lines R59-R61 -
Copy modified lines R63-R64 -
Copy modified lines R80-R82 -
Copy modified lines R84-R85 -
Copy modified lines R101-R103 -
Copy modified lines R105-R106
| @@ -34,8 +34,12 @@ | ||
| return BadRequest("No image file was provided."); | ||
| } | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Analyzing photo. Prompt: {{Prompt}}", prompt); | ||
| var sanitizedPromptForLogging = prompt? | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", " "); | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Analyzing photo. Prompt: {{Prompt}}", sanitizedPromptForLogging); | ||
|
|
||
| // LLM endpoint uses MAF under the hood since we removed SK | ||
| return await AnalyzeWithAgentAsync( | ||
| prompt, | ||
| @@ -53,8 +56,12 @@ | ||
| return BadRequest("No image file was provided."); | ||
| } | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafLocal} Analyzing photo. Prompt: {{Prompt}}", prompt); | ||
| var sanitizedPromptForLogging = prompt? | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", " "); | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafLocal} Analyzing photo. Prompt: {{Prompt}}", sanitizedPromptForLogging); | ||
|
|
||
| return await AnalyzeWithAgentAsync( | ||
| prompt, | ||
| image.FileName, | ||
| @@ -71,8 +77,12 @@ | ||
| return BadRequest("No image file was provided."); | ||
| } | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafFoundry} Analyzing photo. Prompt: {{Prompt}}", prompt); | ||
| var sanitizedPromptForLogging = prompt? | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", " "); | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafFoundry} Analyzing photo. Prompt: {{Prompt}}", sanitizedPromptForLogging); | ||
|
|
||
| return await AnalyzeWithAgentAsync( | ||
| prompt, | ||
| image.FileName, | ||
| @@ -89,8 +98,12 @@ | ||
| return BadRequest("No image file was provided."); | ||
| } | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Analyzing photo. Prompt: {{Prompt}}", prompt); | ||
| var sanitizedPromptForLogging = prompt? | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", " "); | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Analyzing photo. Prompt: {{Prompt}}", sanitizedPromptForLogging); | ||
|
|
||
| return await AnalyzeWithAgentAsync( | ||
| prompt, | ||
| image.FileName, |
| [HttpGet("{customerId}/analyze_maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<CustomerInformation>> GetCustomerMAFOllamaAsync(string customerId, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Getting customer information for ID: {{CustomerId}}", customerId); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to fix log-forging issues, any user-controlled data included in log entries should be sanitized so that it cannot inject line breaks or other control characters that could be misinterpreted as separate log entries or markup. For plain-text logs, the primary concern is to strip or neutralize \r and \n (and possibly other control characters) before logging. For logs that will be displayed as HTML, HTML encoding is appropriate before writing to log storage.
Here, the best fix that preserves existing functionality is to sanitize customerId right before it is logged in GetCustomerMAFOllamaAsync, by removing any newline characters (and, optionally, other control chars) from the string used as the log parameter. This does not change what ID is used for downstream processing (we leave the original customerId when calling GetCustomerFromDataServiceAsync, to avoid altering behavior), but it ensures the logged representation cannot break log formatting. A simple approach, consistent with the recommendation text, is to call Replace to remove \r and \n. Since we are already using string interpolation only for the static prefix and the message template, we can keep the template unchanged and just change the argument we pass to the logger.
Concretely:
- In
GetCustomerMAFOllamaAsync, compute a sanitized variant, e.g.,var safeCustomerId = customerId?.Replace("\r", string.Empty).Replace("\n", string.Empty); - Use
safeCustomerIdas the logged value, while leaving the call toGetCustomerFromDataServiceAsync(customerId, ...)unchanged. - This requires no new imports and no external dependencies.
-
Copy modified lines R52-R54 -
Copy modified lines R56-R57
| @@ -49,8 +49,12 @@ | ||
| [HttpGet("{customerId}/analyze_maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<CustomerInformation>> GetCustomerMAFOllamaAsync(string customerId, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Getting customer information for ID: {{CustomerId}}", customerId); | ||
| var safeCustomerId = customerId? | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Getting customer information for ID: {{CustomerId}}", safeCustomerId); | ||
|
|
||
| // Use DataServiceClient directly instead of Agent | ||
| return await GetCustomerFromDataServiceAsync(customerId, AgentMetadata.LogPrefixes.MafOllama, cancellationToken); | ||
| } |
| [HttpPost("analyze_match_tools/maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<ToolMatchResult>> MatchToolsMAFOllama([FromBody] ToolMatchRequest request) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Matching tools for customer {{CustomerId}}", request.CustomerId); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to fix log forging issues, ensure that any user-controlled strings written to logs are normalized so they cannot inject new log lines or otherwise confuse log parsers. For plain-text logs this typically means removing or replacing newline (\r, \n) and other control characters; for HTML logs this means HTML-encoding user input before logging.
In this file, the problematic pattern is directly logging customerId route parameters and request.CustomerId values. The best fix that doesn’t change existing functionality is to introduce a small helper method in CustomerController that takes a string and returns a sanitized version with newline and carriage-return characters removed (and potentially other control characters if desired). Then, use this helper when passing customerId or request.CustomerId into _logger.LogInformation(...). This preserves the log message structure and parameterization while ensuring that any embedded line breaks are removed before writing to logs.
Concretely:
- Add a private static method
SanitizeForLogging(string value)near the bottom ofCustomerController(but inside the class) that returnsvalue?.Replace("\r", string.Empty).Replace("\n", string.Empty)(or similar). - Update all
_logger.LogInformationcalls that logcustomerIdorrequest.CustomerIdto passSanitizeForLogging(customerId)orSanitizeForLogging(request.CustomerId)as the argument instead of the raw value. - No new imports are needed; we only use
string.Replace, which is inSystemand already available.
-
Copy modified line R25 -
Copy modified line R34 -
Copy modified line R43 -
Copy modified line R61 -
Copy modified line R69 -
Copy modified line R77 -
Copy modified line R85 -
Copy modified lines R94-R106
| @@ -22,7 +22,7 @@ | ||
| [HttpGet("{customerId}/analyze_llm")] | ||
| public async Task<ActionResult<CustomerInformation>> GetCustomerLlmAsync(string customerId, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Getting customer information for ID: {{CustomerId}}", customerId); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Getting customer information for ID: {{CustomerId}}", SanitizeForLogging(customerId)); | ||
|
|
||
| // Use DataServiceClient directly instead of LLM/Agent | ||
| return await GetCustomerFromDataServiceAsync(customerId, AgentMetadata.LogPrefixes.Llm, cancellationToken); | ||
| @@ -31,7 +31,7 @@ | ||
| [HttpGet("{customerId}/analyze_maf_local")] // Using constant AgentMetadata.FrameworkIdentifiers.MafLocal | ||
| public async Task<ActionResult<CustomerInformation>> GetCustomerMAFLocalAsync(string customerId, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafLocal} Getting customer information for ID: {{CustomerId}}", customerId); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafLocal} Getting customer information for ID: {{CustomerId}}", SanitizeForLogging(customerId)); | ||
|
|
||
| // Use DataServiceClient directly instead of Agent | ||
| return await GetCustomerFromDataServiceAsync(customerId, AgentMetadata.LogPrefixes.MafLocal, cancellationToken); | ||
| @@ -40,7 +40,7 @@ | ||
| [HttpGet("{customerId}/analyze_maf_foundry")] // Using constant AgentMetadata.FrameworkIdentifiers.MafFoundry | ||
| public async Task<ActionResult<CustomerInformation>> GetCustomerMAFFoundryAsync(string customerId, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafFoundry} Getting customer information for ID: {{CustomerId}}", customerId); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafFoundry} Getting customer information for ID: {{CustomerId}}", SanitizeForLogging(customerId)); | ||
|
|
||
| // Use DataServiceClient directly instead of Agent | ||
| return await GetCustomerFromDataServiceAsync(customerId, AgentMetadata.LogPrefixes.MafFoundry, cancellationToken); | ||
| @@ -58,7 +58,7 @@ | ||
| [HttpPost("analyze_match_tools/llm")] | ||
| public async Task<ActionResult<ToolMatchResult>> MatchToolsLlm([FromBody] ToolMatchRequest request) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Matching tools for customer {{CustomerId}}", request.CustomerId); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Matching tools for customer {{CustomerId}}", SanitizeForLogging(request.CustomerId)); | ||
| // Use DataServiceClient to get customer information | ||
| return await MatchToolsInternal(request, AgentMetadata.LogPrefixes.Llm); | ||
| } | ||
| @@ -66,7 +66,7 @@ | ||
| [HttpPost("analyze_match_tools/maf_local")] // Using constant AgentMetadata.FrameworkIdentifiers.MafLocal | ||
| public async Task<ActionResult<ToolMatchResult>> MatchToolsMAF([FromBody] ToolMatchRequest request) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafLocal} Matching tools for customer {{CustomerId}}", request.CustomerId); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafLocal} Matching tools for customer {{CustomerId}}", SanitizeForLogging(request.CustomerId)); | ||
| // Use DataServiceClient to get customer information | ||
| return await MatchToolsInternal(request, AgentMetadata.LogPrefixes.MafLocal); | ||
| } | ||
| @@ -74,7 +74,7 @@ | ||
| [HttpPost("analyze_match_tools/maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<ToolMatchResult>> MatchToolsMAFOllama([FromBody] ToolMatchRequest request) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Matching tools for customer {{CustomerId}}", request.CustomerId); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Matching tools for customer {{CustomerId}}", SanitizeForLogging(request.CustomerId)); | ||
| // Use DataServiceClient to get customer information | ||
| return await MatchToolsInternal(request, AgentMetadata.LogPrefixes.MafOllama); | ||
| } | ||
| @@ -82,7 +82,7 @@ | ||
| [HttpGet("{customerId}/analyze_direct_call")] | ||
| public async Task<ActionResult<CustomerInformation>> GetCustomerDirectCallAsync(string customerId) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.DirectCall} Getting customer information for ID: {{CustomerId}}", customerId); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.DirectCall} Getting customer information for ID: {{CustomerId}}", SanitizeForLogging(customerId)); | ||
|
|
||
| // add a sleep of 1 seconds to emulate the analysis time | ||
| Thread.Sleep(1000); | ||
| @@ -91,6 +91,19 @@ | ||
| return await GetCustomerFromDataServiceAsync(customerId, AgentMetadata.LogPrefixes.DirectCall, CancellationToken.None); | ||
| } | ||
|
|
||
| private static string SanitizeForLogging(string value) | ||
| { | ||
| if (value == null) | ||
| { | ||
| return string.Empty; | ||
| } | ||
|
|
||
| // Remove characters that could be used to forge or obscure log entries | ||
| return value | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
| } | ||
|
|
||
| [HttpPost("analyze_match_tools/direct_call")] | ||
| public async Task<ActionResult<ToolMatchResult>> MatchToolsDirectCall([FromBody] ToolMatchRequest request) | ||
| { |
| [HttpPost("analyze_search_maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<ToolRecommendation[]>> SearchInventoryMAFOllamaAsync([FromBody] InventorySearchRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Searching inventory for query: {{SearchQuery}}", request.SearchQuery); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to prevent log forging when logging user-provided strings in plain-text-oriented logs, normalize those strings before logging: remove newline and carriage-return characters (and optionally other control characters), or perform appropriate encoding. This ensures a malicious value cannot inject additional log lines or otherwise alter log structure.
For this specific file, we should sanitize request.SearchQuery before passing it to _logger.LogInformation in all endpoints that log it. The simplest, non–behaviour-breaking fix is to create a local variable that holds a sanitized version of SearchQuery, using Replace to strip Environment.NewLine, \n, and \r, and then log that sanitized value. We will make this change in each method that logs request.SearchQuery:
SearchInventoryLlmAsyncSearchInventoryMAFLocalAsyncSearchInventoryMAFFoundryAsyncSearchInventoryMAFOllamaAsyncSearchInventoryDirectCallAsync
No new imports are needed; we can use Environment.NewLine and string Replace directly. The rest of the application logic (calls to SearchInventoryFromDataServiceAsync, return types, etc.) remains unchanged.
-
Copy modified lines R25-R28 -
Copy modified lines R30-R31 -
Copy modified lines R38-R41 -
Copy modified lines R43-R44 -
Copy modified lines R51-R54 -
Copy modified lines R56-R57 -
Copy modified lines R64-R67 -
Copy modified lines R69-R70 -
Copy modified lines R77-R80 -
Copy modified lines R82-R83
| @@ -22,8 +22,13 @@ | ||
| [HttpPost("analyze_search_llm")] | ||
| public async Task<ActionResult<ToolRecommendation[]>> SearchInventoryLlmAsync([FromBody] InventorySearchRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Searching inventory for query: {{SearchQuery}}", request.SearchQuery); | ||
| var sanitizedSearchQuery = request.SearchQuery? | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty); | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Searching inventory for query: {{SearchQuery}}", sanitizedSearchQuery); | ||
|
|
||
| // Use DataServiceClient directly instead of LLM/Agent | ||
| return await SearchInventoryFromDataServiceAsync(request, AgentMetadata.LogPrefixes.Llm, cancellationToken); | ||
| } | ||
| @@ -31,8 +35,13 @@ | ||
| [HttpPost("analyze_search_maf_local")] // Using constant AgentMetadata.FrameworkIdentifiers.MafLocal | ||
| public async Task<ActionResult<ToolRecommendation[]>> SearchInventoryMAFLocalAsync([FromBody] InventorySearchRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafLocal} Searching inventory for query: {{SearchQuery}}", request.SearchQuery); | ||
| var sanitizedSearchQuery = request.SearchQuery? | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty); | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafLocal} Searching inventory for query: {{SearchQuery}}", sanitizedSearchQuery); | ||
|
|
||
| // Use DataServiceClient directly instead of Agent | ||
| return await SearchInventoryFromDataServiceAsync(request, AgentMetadata.LogPrefixes.MafLocal, cancellationToken); | ||
| } | ||
| @@ -40,8 +48,13 @@ | ||
| [HttpPost("analyze_search_maf_foundry")] // Using constant AgentMetadata.FrameworkIdentifiers.MafFoundry | ||
| public async Task<ActionResult<ToolRecommendation[]>> SearchInventoryMAFFoundryAsync([FromBody] InventorySearchRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafFoundry} Searching inventory for query: {{SearchQuery}}", request.SearchQuery); | ||
| var sanitizedSearchQuery = request.SearchQuery? | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty); | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafFoundry} Searching inventory for query: {{SearchQuery}}", sanitizedSearchQuery); | ||
|
|
||
| // Use DataServiceClient directly instead of Agent | ||
| return await SearchInventoryFromDataServiceAsync(request, AgentMetadata.LogPrefixes.MafFoundry, cancellationToken); | ||
| } | ||
| @@ -49,8 +61,13 @@ | ||
| [HttpPost("analyze_search_maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<ToolRecommendation[]>> SearchInventoryMAFOllamaAsync([FromBody] InventorySearchRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Searching inventory for query: {{SearchQuery}}", request.SearchQuery); | ||
| var sanitizedSearchQuery = request.SearchQuery? | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty); | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Searching inventory for query: {{SearchQuery}}", sanitizedSearchQuery); | ||
|
|
||
| // Use DataServiceClient directly instead of Agent | ||
| return await SearchInventoryFromDataServiceAsync(request, AgentMetadata.LogPrefixes.MafOllama, cancellationToken); | ||
| } | ||
| @@ -58,8 +74,13 @@ | ||
| [HttpPost("analyze_search_direct_call")] | ||
| public async Task<ActionResult<ToolRecommendation[]>> SearchInventoryDirectCallAsync([FromBody] InventorySearchRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.DirectCall} Searching inventory for query: {{SearchQuery}}", request.SearchQuery); | ||
| var sanitizedSearchQuery = request.SearchQuery? | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty); | ||
|
|
||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.DirectCall} Searching inventory for query: {{SearchQuery}}", sanitizedSearchQuery); | ||
|
|
||
| // Use DataServiceClient to search inventory | ||
| return await SearchInventoryFromDataServiceAsync(request, AgentMetadata.LogPrefixes.DirectCall, cancellationToken); | ||
| } |
| [HttpGet("analyze_find/maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<LocationResult>> FindProductLocationMAFOllamaAsync([FromQuery] string product, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Finding location for product: {{Product}}", product); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
To fix this, sanitize the user-provided product string before logging it. For plain-text logs, the main concern is newline (and possibly carriage return) characters that allow an attacker to visually forge additional log entries. We should create a sanitized version of product with these characters removed (or replaced) and use this sanitized value in all log calls that include product. This keeps existing behavior—searching and agent calls—unchanged because the raw product is still used for business logic; only what is logged is normalized.
The best minimally invasive fix in this file is:
- In each public action method that logs
product(FindProductLocationLlmAsync,FindProductLocationMAFAsync, andFindProductLocationMAFOllamaAsync), compute a localsafeProductby removing\rand\nfromproduct. - Use
safeProductinstead ofproductin the corresponding_logger.LogInformationcalls. - Keep the rest of the flow (
FindProductLocationAsync, agents, etc.) unchanged so functionality is preserved.
No new helper methods or external dependencies are required; we can use string.Replace directly in these methods.
-
Copy modified lines R35-R36 -
Copy modified lines R49-R50 -
Copy modified lines R62-R63
| @@ -32,7 +32,8 @@ | ||
| [HttpGet("analyze_find/llm")] | ||
| public async Task<ActionResult<LocationResult>> FindProductLocationLlmAsync([FromQuery] string product, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Finding location for product: {{Product}}", product); | ||
| var safeProduct = product?.Replace("\r", string.Empty).Replace("\n", string.Empty); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Finding location for product: {{Product}}", safeProduct); | ||
|
|
||
| // LLM endpoint uses MAF under the hood since we removed SK | ||
| return await FindProductLocationAsync( | ||
| @@ -45,7 +46,8 @@ | ||
| [HttpGet("analyze_find/maf")] // Using constant AgentMetadata.FrameworkIdentifiers.Maf | ||
| public async Task<ActionResult<LocationResult>> FindProductLocationMAFAsync([FromQuery] string product, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Maf} Finding location for product: {{Product}}", product); | ||
| var safeProduct = product?.Replace("\r", string.Empty).Replace("\n", string.Empty); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Maf} Finding location for product: {{Product}}", safeProduct); | ||
|
|
||
| return await FindProductLocationAsync( | ||
| product, | ||
| @@ -57,7 +59,8 @@ | ||
| [HttpGet("analyze_find/maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<LocationResult>> FindProductLocationMAFOllamaAsync([FromQuery] string product, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Finding location for product: {{Product}}", product); | ||
| var safeProduct = product?.Replace("\r", string.Empty).Replace("\n", string.Empty); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Finding location for product: {{Product}}", safeProduct); | ||
|
|
||
| return await FindProductLocationAsync( | ||
| product, |
| [HttpPost("analyze_alternatives/maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<MatchmakingResult>> FindAlternativesMAFOllamaAsync([FromBody] AlternativesRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", request.ProductQuery, request.UserId); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to prevent log forging with plain-text logs, all user-controlled strings included in log messages should be normalized so they cannot inject unexpected line breaks or other control characters that affect how log entries are displayed. The simplest robust mitigation here is to remove or replace newline sequences (\r, \n, and Environment.NewLine) from request.ProductQuery before passing it to the logger, while leaving the rest of the logging behavior intact.
The best fix with minimal functional impact is to introduce a small, local helper method inside MatchmakingController that takes a string and returns a “log-safe” version with any newline characters stripped. We then use this helper for all _logger.LogInformation calls that include request.ProductQuery. This way, we don’t change business logic, only how the potentially dangerous value is represented in logs. Concretely:
- Add a private method such as
SanitizeForLog(string input)toMatchmakingControllerthat replaces"\r\n","\n", and"\r"with empty strings (or spaces). - Update the four logging calls in
FindAlternativesLlmAsync,FindAlternativesMAFAsync,FindAlternativesMAFOllamaAsync, andFindAlternativesDirectCallAsyncto passSanitizeForLog(request.ProductQuery)instead ofrequest.ProductQuery.
No new imports are needed; we usestring.Replace, which is already available.
-
Copy modified lines R31-R44 -
Copy modified line R48 -
Copy modified line R58 -
Copy modified line R70 -
Copy modified line R82
| @@ -28,10 +28,24 @@ | ||
| _ollamaAgent = ollamaAgentProvider.GetAgentByName(AgentMetadata.GetAgentName(AgentType.ProductMatchmakingAgent)); | ||
| } | ||
|
|
||
| private static string SanitizeForLog(string? input) | ||
| { | ||
| if (string.IsNullOrEmpty(input)) | ||
| { | ||
| return input ?? string.Empty; | ||
| } | ||
|
|
||
| // Remove line breaks to prevent log forging via injected new log lines. | ||
| return input | ||
| .Replace("\r\n", string.Empty) | ||
| .Replace("\n", string.Empty) | ||
| .Replace("\r", string.Empty); | ||
| } | ||
|
|
||
| [HttpPost("analyze_alternatives/llm")] | ||
| public async Task<ActionResult<MatchmakingResult>> FindAlternativesLlmAsync([FromBody] AlternativesRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", request.ProductQuery, request.UserId); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", SanitizeForLog(request.ProductQuery), request.UserId); | ||
|
|
||
| // LLM endpoint uses MAF under the hood since we removed SK | ||
| return await FindAlternativesAsync( | ||
| @@ -44,7 +55,7 @@ | ||
| [HttpPost("analyze_alternatives/maf")] // Using constant AgentMetadata.FrameworkIdentifiers.Maf | ||
| public async Task<ActionResult<MatchmakingResult>> FindAlternativesMAFAsync([FromBody] AlternativesRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Maf} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", request.ProductQuery, request.UserId); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Maf} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", SanitizeForLog(request.ProductQuery), request.UserId); | ||
|
|
||
| return await FindAlternativesAsync( | ||
| request, | ||
| @@ -56,7 +67,7 @@ | ||
| [HttpPost("analyze_alternatives/maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<MatchmakingResult>> FindAlternativesMAFOllamaAsync([FromBody] AlternativesRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", request.ProductQuery, request.UserId); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", SanitizeForLog(request.ProductQuery), request.UserId); | ||
|
|
||
| return await FindAlternativesAsync( | ||
| request, | ||
| @@ -68,7 +79,7 @@ | ||
| [HttpPost("analyze_alternatives/direct_call")] | ||
| public ActionResult<MatchmakingResult> FindAlternativesDirectCallAsync([FromBody] AlternativesRequest request) | ||
| { | ||
| _logger.LogInformation("[DirectCall] Finding alternatives for product: {ProductQuery}, User: {UserId}", request.ProductQuery, request.UserId); | ||
| _logger.LogInformation("[DirectCall] Finding alternatives for product: {ProductQuery}, User: {UserId}", SanitizeForLog(request.ProductQuery), request.UserId); | ||
|
|
||
| // DirectCall mode bypasses AI orchestration and returns fallback response immediately | ||
| return Ok(BuildFallbackResult(request)); |
| [HttpPost("analyze_alternatives/maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<MatchmakingResult>> FindAlternativesMAFOllamaAsync([FromBody] AlternativesRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", request.ProductQuery, request.UserId); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to fix log forging issues, any user-controlled data that is written to logs should be normalized so that it cannot inject additional log entries or otherwise break the log format. For plain-text logs, the most important step is to remove or replace newline and carriage-return characters (and sometimes other control characters) from user input before logging. When logs are later displayed in HTML, HTML encoding should also be applied.
For this specific controller, the issue arises from logging request.UserId (and similarly request.ProductQuery) directly. The least intrusive fix that does not change existing functionality is to sanitize these string values just before logging, by stripping \r and \n characters. We can implement a small private helper method inside MatchmakingController like SanitizeForLogging(string value) that returns the value with newlines and carriage returns removed (or replaced with spaces). We then call this helper for request.ProductQuery and request.UserId in all four logging calls in this file. This keeps behavior the same for normal inputs while preventing attackers from injecting additional log lines. No new external dependencies are required; we can use string.Replace from System (already available) and define the helper as a private method of the controller.
Concretely:
- In
MatchmakingController, add a private methodSanitizeForLogging(string? value)that replaces\rand\nwith empty strings (or spaces), and safely handlesnull. - Update the
_logger.LogInformationcalls in:FindAlternativesLlmAsyncFindAlternativesMAFAsyncFindAlternativesMAFOllamaAsyncFindAlternativesDirectCallAsync
to passSanitizeForLogging(request.ProductQuery)andSanitizeForLogging(request.UserId)instead of the raw properties.
- Keep the log templates as-is, preserving existing structured logging and semantics.
All changes will be confined to src/MatchmakingService/Controllers/MatchmakingController.cs in the shown snippet region.
-
Copy modified lines R31-R42 -
Copy modified lines R46-R49 -
Copy modified lines R59-R62 -
Copy modified lines R74-R77 -
Copy modified lines R89-R92
| @@ -28,10 +28,25 @@ | ||
| _ollamaAgent = ollamaAgentProvider.GetAgentByName(AgentMetadata.GetAgentName(AgentType.ProductMatchmakingAgent)); | ||
| } | ||
|
|
||
| private static string? SanitizeForLogging(string? value) | ||
| { | ||
| if (string.IsNullOrEmpty(value)) | ||
| { | ||
| return value; | ||
| } | ||
|
|
||
| return value | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
| } | ||
|
|
||
| [HttpPost("analyze_alternatives/llm")] | ||
| public async Task<ActionResult<MatchmakingResult>> FindAlternativesLlmAsync([FromBody] AlternativesRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", request.ProductQuery, request.UserId); | ||
| _logger.LogInformation( | ||
| $"{AgentMetadata.LogPrefixes.Llm} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", | ||
| SanitizeForLogging(request.ProductQuery), | ||
| SanitizeForLogging(request.UserId)); | ||
|
|
||
| // LLM endpoint uses MAF under the hood since we removed SK | ||
| return await FindAlternativesAsync( | ||
| @@ -44,7 +56,10 @@ | ||
| [HttpPost("analyze_alternatives/maf")] // Using constant AgentMetadata.FrameworkIdentifiers.Maf | ||
| public async Task<ActionResult<MatchmakingResult>> FindAlternativesMAFAsync([FromBody] AlternativesRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Maf} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", request.ProductQuery, request.UserId); | ||
| _logger.LogInformation( | ||
| $"{AgentMetadata.LogPrefixes.Maf} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", | ||
| SanitizeForLogging(request.ProductQuery), | ||
| SanitizeForLogging(request.UserId)); | ||
|
|
||
| return await FindAlternativesAsync( | ||
| request, | ||
| @@ -56,7 +71,10 @@ | ||
| [HttpPost("analyze_alternatives/maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<MatchmakingResult>> FindAlternativesMAFOllamaAsync([FromBody] AlternativesRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", request.ProductQuery, request.UserId); | ||
| _logger.LogInformation( | ||
| $"{AgentMetadata.LogPrefixes.MafOllama} Finding alternatives for product: {{ProductQuery}}, User: {{UserId}}", | ||
| SanitizeForLogging(request.ProductQuery), | ||
| SanitizeForLogging(request.UserId)); | ||
|
|
||
| return await FindAlternativesAsync( | ||
| request, | ||
| @@ -68,7 +86,10 @@ | ||
| [HttpPost("analyze_alternatives/direct_call")] | ||
| public ActionResult<MatchmakingResult> FindAlternativesDirectCallAsync([FromBody] AlternativesRequest request) | ||
| { | ||
| _logger.LogInformation("[DirectCall] Finding alternatives for product: {ProductQuery}, User: {UserId}", request.ProductQuery, request.UserId); | ||
| _logger.LogInformation( | ||
| "[DirectCall] Finding alternatives for product: {ProductQuery}, User: {UserId}", | ||
| SanitizeForLogging(request.ProductQuery), | ||
| SanitizeForLogging(request.UserId)); | ||
|
|
||
| // DirectCall mode bypasses AI orchestration and returns fallback response immediately | ||
| return Ok(BuildFallbackResult(request)); |
| [HttpPost("analyze_search/maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<ToolRecommendation[]>> SearchInventoryMAFOllamaAsync([FromBody] InventorySearchRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Searching inventory for query: {{SearchQuery}}", request.SearchQuery); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to fix log-forging issues, sanitize or encode any user-controlled string before logging it. For plain-text log outputs, remove or normalize newline and other control characters so a single log entry cannot visually or structurally turn into multiple entries. For HTML outputs, HTML-encode the value.
In this file, the simplest and safest fix without changing functionality is to sanitize request.SearchQuery once in each action before passing it to _logger.LogInformation. We can define a small helper method inside ProductSearchController that removes carriage returns and newlines (and optionally other control characters) from a string. Then, replace direct uses of request.SearchQuery in log calls with SanitizeForLogging(request.SearchQuery). The rest of the behavior (the actual search and the return values) remains unchanged; we only adjust what gets logged.
Concretely:
- In
ProductSearchController, add a private static helper method, e.g.,SanitizeForLogging(string input), that returnsinputwith\rand\nremoved (or replaced with spaces). - Update the four logging calls in this file:
- Line 31: pass
SanitizeForLogging(request.SearchQuery)as the structured parameter. - Line 44: same change.
- Line 56: same change (this is the one flagged).
- Line 68: same change for the DirectCall route.
No new external dependencies are needed; we can use basicstring.Replace.
- Line 31: pass
-
Copy modified lines R28-R39 -
Copy modified line R43 -
Copy modified line R53 -
Copy modified line R65 -
Copy modified line R77
| @@ -25,10 +25,22 @@ | ||
| _ollamaAgent = ollamaAgentProvider.GetAgentByName(AgentMetadata.GetAgentName(AgentType.ProductSearchAgent)); | ||
| } | ||
|
|
||
| private static string SanitizeForLogging(string? input) | ||
| { | ||
| if (input == null) | ||
| { | ||
| return string.Empty; | ||
| } | ||
|
|
||
| // Remove newline characters to prevent log forging via line breaks. | ||
| return input.Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
| } | ||
|
|
||
| [HttpPost("analyze_search/llm")] | ||
| public async Task<ActionResult<ToolRecommendation[]>> SearchInventoryLlmAsync([FromBody] InventorySearchRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Searching inventory for query: {{SearchQuery}}", request.SearchQuery); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Llm} Searching inventory for query: {{SearchQuery}}", SanitizeForLogging(request.SearchQuery)); | ||
|
|
||
| // LLM endpoint uses MAF under the hood since we removed SK | ||
| return await SearchProductsAsync( | ||
| @@ -41,7 +50,7 @@ | ||
| [HttpPost("analyze_search/maf")] // Using constant AgentMetadata.FrameworkIdentifiers.Maf | ||
| public async Task<ActionResult<ToolRecommendation[]>> SearchInventoryMAFAsync([FromBody] InventorySearchRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Maf} Searching inventory for query: {{SearchQuery}}", request.SearchQuery); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.Maf} Searching inventory for query: {{SearchQuery}}", SanitizeForLogging(request.SearchQuery)); | ||
|
|
||
| return await SearchProductsAsync( | ||
| request, | ||
| @@ -53,7 +62,7 @@ | ||
| [HttpPost("analyze_search/maf_ollama")] // Using constant AgentMetadata.FrameworkIdentifiers.MafOllama | ||
| public async Task<ActionResult<ToolRecommendation[]>> SearchInventoryMAFOllamaAsync([FromBody] InventorySearchRequest request, CancellationToken cancellationToken) | ||
| { | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Searching inventory for query: {{SearchQuery}}", request.SearchQuery); | ||
| _logger.LogInformation($"{AgentMetadata.LogPrefixes.MafOllama} Searching inventory for query: {{SearchQuery}}", SanitizeForLogging(request.SearchQuery)); | ||
|
|
||
| return await SearchProductsAsync( | ||
| request, | ||
| @@ -65,7 +74,7 @@ | ||
| [HttpPost("analyze_search/direct_call")] | ||
| public ActionResult<ToolRecommendation[]> SearchInventoryDirectCallAsync([FromBody] InventorySearchRequest request) | ||
| { | ||
| _logger.LogInformation("[DirectCall] Searching inventory for query: {SearchQuery}", request.SearchQuery); | ||
| _logger.LogInformation("[DirectCall] Searching inventory for query: {SearchQuery}", SanitizeForLogging(request.SearchQuery)); | ||
|
|
||
| // DirectCall mode bypasses AI orchestration and returns fallback response immediately | ||
| return Ok(BuildFallbackRecommendations(request.SearchQuery)); |
Introduce "HandOffWorkflow" and "GroupChatWorkflow" as keyed singletons in MAFLocalAgentProvider, centralizing their construction and registration. Update MultiAgentControllerMAFLocal to use the registered HandOffWorkflow instead of building it inline. Add WorkflowExtensions with a SetName method using reflection to set workflow names, addressing DevUI issues with missing workflow names. GroupChatWorkflow uses RoundRobinGroupChatManager with all four agents as participants. Clarify workflow registration comments.
Implements the
MafOllamaworking mode as specified indocs/plans/ollama-working-mode-plan.md, enabling agents to use locally hosted Ollama models via OllamaSharp.Changes
New
ZavaMAFOllamaprojectMAFOllamaAgentProviderwithAddMAFOllamaAgents()andAddMAFOllamaWorkflows()extensions"ollama") to allow multipleIChatClientimplementationshttp://localhost:11434, model:ministral-3(configurable viaOllama__Endpoint,Ollama__ChatModel)Controllers
SingleAgentControllerMAFOllama→/api/singleagent/maf_ollamaMultiAgentControllerMAFOllama→/api/multiagent/maf_ollamaWorkingMode enum
MafOllamawith short namemaf_ollama, display name "MAF - Ollama"AllModes,Parse(),TryParse()AgentMetadata
FrameworkIdentifiers.MafOllama,LogPrefixes.MafOllamaGetOllamaAgentName()methodStore frontend services
SingleAgentService.csandMultiAgentService.csendpoint mappingsUsage
Frontend mode selector auto-populates from
WorkingModeProvider.GetAllModes().Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.