Function bodies 513 total
openai.Provider.convertUserMessages method · go · L244-L269 (26 LOC)internal/provider/openai/provider.go
func (p *Provider) convertUserMessages(msg provider.Message) []apiMessage {
var toolResults []apiMessage
var texts []string
for _, block := range msg.Content {
switch block.Type {
case "tool_result":
toolResults = append(toolResults, apiMessage{
Role: "tool",
Content: block.Text,
ToolCallID: block.ToolUseID,
})
case "text":
texts = append(texts, block.Text)
}
}
if len(toolResults) > 0 {
return toolResults
}
return []apiMessage{{
Role: "user",
Content: strings.Join(texts, ""),
}}
}openai.Provider.processStream method · go · L272-L361 (90 LOC)internal/provider/openai/provider.go
func (p *Provider) processStream(ctx context.Context, body io.ReadCloser, ch chan<- provider.StreamEvent) {
defer close(ch)
defer body.Close()
scanner := bufio.NewScanner(body)
for scanner.Scan() {
if ctx.Err() != nil {
select {
case ch <- provider.StreamEvent{Type: "error", Error: ctx.Err()}:
default:
}
return
}
line := scanner.Text()
// Skip empty lines and non-data lines
if !strings.HasPrefix(line, "data: ") {
continue
}
data := strings.TrimPrefix(line, "data: ")
// Check for end of stream
if data == "[DONE]" {
select {
case ch <- provider.StreamEvent{Type: "stop"}:
case <-ctx.Done():
}
return
}
var chunk chatChunk
if err := json.Unmarshal([]byte(data), &chunk); err != nil {
select {
case ch <- provider.StreamEvent{Type: "error", Error: fmt.Errorf("parsing chunk: %w", err)}:
case <-ctx.Done():
}
continue
}
if len(chunk.Choices) == 0 {
continue
}
delta := chunk.Choices[0].Delta
// Handleprovider.NewUserMessage function · go · L63-L73 (11 LOC)internal/provider/types.go
func NewUserMessage(text string) Message {
return Message{
Role: "user",
Content: []ContentBlock{
{
Type: "text",
Text: text,
},
},
}
}provider.NewToolResultMessage function · go · L76-L88 (13 LOC)internal/provider/types.go
func NewToolResultMessage(toolUseID, content string, isError bool) Message {
return Message{
Role: "user",
Content: []ContentBlock{
{
Type: "tool_result",
ToolUseID: toolUseID,
Text: content,
IsError: isError,
},
},
}
}runner.ExitCodeFromFindings function · go · L22-L36 (15 LOC)internal/runner/exitcode.go
func ExitCodeFromFindings(findings []output.SecurityFinding, failOn string) int {
if failOn == "" {
return 0
}
threshold := security.SeverityRank(security.Severity(failOn))
if threshold == 0 {
return 0
}
for _, f := range findings {
if security.SeverityRank(security.Severity(f.Severity)) >= threshold {
return 1
}
}
return 0
}runner.HeadlessRunner.Run method · go · L28-L86 (59 LOC)internal/runner/headless.go
func (r *HeadlessRunner) Run(ctx context.Context, prompt, mode string) (*output.RunResult, error) {
start := time.Now()
ch, err := r.turn(ctx, prompt)
if err != nil {
return &output.RunResult{
Prompt: prompt,
Mode: mode,
DurationMs: time.Since(start).Milliseconds(),
Error: err.Error(),
}, nil
}
var textBuf strings.Builder
var toolCalls []output.ToolCallLog
var lastErr string
turns := 0
for evt := range ch {
switch evt.Type {
case "text_delta":
textBuf.WriteString(evt.Text)
case "tool_call":
if evt.ToolCall != nil {
toolCalls = append(toolCalls, output.ToolCallLog{
ID: evt.ToolCall.ID,
Name: evt.ToolCall.Name,
Input: json.RawMessage(evt.ToolCall.Input),
})
}
case "tool_result":
if evt.ToolResult != nil {
for i := range toolCalls {
if toolCalls[i].ID == evt.ToolResult.ID {
toolCalls[i].Result = evt.ToolResult.Content
toolCalls[i].IsError = evt.ToolResult.IsError
break
runner.ResolveInput function · go · L14-L42 (29 LOC)internal/runner/input.go
func ResolveInput(promptFlag, filePath string, stdinReader io.Reader) (string, error) {
if text := strings.TrimSpace(promptFlag); text != "" {
return text, nil
}
if filePath != "" {
data, err := os.ReadFile(filePath)
if err != nil {
return "", fmt.Errorf("reading prompt file: %w", err)
}
text := strings.TrimSpace(string(data))
if text == "" {
return "", fmt.Errorf("prompt file is empty: %s", filePath)
}
return text, nil
}
if stdinReader != nil {
data, err := io.ReadAll(stdinReader)
if err != nil {
return "", fmt.Errorf("reading stdin: %w", err)
}
if text := strings.TrimSpace(string(data)); text != "" {
return text, nil
}
}
return "", fmt.Errorf("no input provided: use --prompt, --file, or pipe to stdin")
}Powered by Repobility — scan your code at https://repobility.com
analyzer.NewAuthAnalyzer function · go · L37-L44 (8 LOC)internal/security/analyzer/auth.go
func NewAuthAnalyzer(p provider.LLMProvider) *baseAnalyzer {
return &baseAnalyzer{
name: "auth-authz",
category: security.CategoryAuthentication,
systemPrompt: authSystemPrompt,
provider: p,
}
}analyzer.baseAnalyzer.Analyze method · go · L53-L92 (40 LOC)internal/security/analyzer/base.go
func (b *baseAnalyzer) Analyze(ctx context.Context, chunks []security.AnalysisChunk) ([]security.Finding, error) {
if len(chunks) == 0 {
return nil, nil
}
userContent := buildUserMessage(chunks)
req := provider.CompletionRequest{
System: b.systemPrompt,
Messages: []provider.Message{
provider.NewUserMessage(userContent),
},
MaxTokens: 4096,
}
ch, err := b.provider.Stream(ctx, req)
if err != nil {
return nil, fmt.Errorf("%s analyzer: stream failed: %w", b.name, err)
}
response := collectStreamResponse(ch)
parsed, parseErr := parseFindings(response)
if parseErr != nil {
return []security.Finding{
{
ID: fmt.Sprintf("%s-unparsed-001", b.name),
Scanner: b.name,
Severity: security.SeverityInfo,
Category: b.category,
Title: "Unparseable LLM response",
Description: "The LLM response could not be parsed as JSON findings.",
Evidence: response,
Confidence: security.ConfidenceLow,
},
}, nil
}
reanalyzer.collectStreamResponse function · go · L106-L114 (9 LOC)internal/security/analyzer/base.go
func collectStreamResponse(ch <-chan provider.StreamEvent) string {
var sb strings.Builder
for event := range ch {
if event.Type == "text_delta" {
sb.WriteString(event.Text)
}
}
return sb.String()
}analyzer.parseFindings function · go · L118-L140 (23 LOC)internal/security/analyzer/base.go
func parseFindings(response string) ([]analyzedFinding, error) {
trimmed := strings.TrimSpace(response)
// Strip markdown code fences if present.
if strings.HasPrefix(trimmed, "```") {
lines := strings.Split(trimmed, "\n")
if len(lines) >= 2 {
// Remove first line (```json) and last line (```)
start := 1
end := len(lines)
if strings.TrimSpace(lines[end-1]) == "```" {
end--
}
trimmed = strings.Join(lines[start:end], "\n")
}
}
var findings []analyzedFinding
if err := json.Unmarshal([]byte(trimmed), &findings); err != nil {
return nil, fmt.Errorf("parse findings JSON: %w", err)
}
return findings, nil
}analyzer.baseAnalyzer.mapFindings method · go · L143-L166 (24 LOC)internal/security/analyzer/base.go
func (b *baseAnalyzer) mapFindings(parsed []analyzedFinding) []security.Finding {
findings := make([]security.Finding, 0, len(parsed))
for _, p := range parsed {
f := security.Finding{
ID: p.ID,
Scanner: b.name,
Severity: mapSeverity(p.Severity),
Category: b.category,
Title: p.Title,
Description: p.Description,
Location: security.Location{
File: p.Location.File,
StartLine: p.Location.StartLine,
EndLine: p.Location.EndLine,
Function: p.Location.Function,
},
CWE: p.CWE,
Confidence: mapConfidence(p.Confidence),
Remediation: p.Remediation,
}
findings = append(findings, f)
}
return findings
}analyzer.mapSeverity function · go · L169-L184 (16 LOC)internal/security/analyzer/base.go
func mapSeverity(s string) security.Severity {
switch strings.ToLower(s) {
case "critical":
return security.SeverityCritical
case "high":
return security.SeverityHigh
case "medium":
return security.SeverityMedium
case "low":
return security.SeverityLow
case "info":
return security.SeverityInfo
default:
return security.SeverityInfo
}
}analyzer.mapConfidence function · go · L187-L198 (12 LOC)internal/security/analyzer/base.go
func mapConfidence(c string) security.Confidence {
switch strings.ToLower(c) {
case "high":
return security.ConfidenceHigh
case "medium":
return security.ConfidenceMedium
case "low":
return security.ConfidenceLow
default:
return security.ConfidenceLow
}
}analyzer.NewBusinessLogicAnalyzer function · go · L37-L44 (8 LOC)internal/security/analyzer/business.go
func NewBusinessLogicAnalyzer(p provider.LLMProvider) *baseAnalyzer {
return &baseAnalyzer{
name: "business-logic",
category: security.CategoryInputValidation,
systemPrompt: businessSystemPrompt,
provider: p,
}
}Repobility · MCP-ready · https://repobility.com
analyzer.NewConcurrencyAnalyzer function · go · L38-L45 (8 LOC)internal/security/analyzer/concurrency.go
func NewConcurrencyAnalyzer(p provider.LLMProvider) *baseAnalyzer {
return &baseAnalyzer{
name: "concurrency",
category: security.CategoryRaceCondition,
systemPrompt: concurrencySystemPrompt,
provider: p,
}
}analyzer.NewCryptoAnalyzer function · go · L38-L45 (8 LOC)internal/security/analyzer/crypto.go
func NewCryptoAnalyzer(p provider.LLMProvider) *baseAnalyzer {
return &baseAnalyzer{
name: "cryptography",
category: security.CategoryCryptography,
systemPrompt: cryptoSystemPrompt,
provider: p,
}
}analyzer.NewDataFlowAnalyzer function · go · L35-L42 (8 LOC)internal/security/analyzer/dataflow.go
func NewDataFlowAnalyzer(p provider.LLMProvider) *baseAnalyzer {
return &baseAnalyzer{
name: "dataflow",
category: security.CategoryInjection,
systemPrompt: dataflowSystemPrompt,
provider: p,
}
}analyzer.NewSkillAnalyzerAdapter function · go · L22-L28 (7 LOC)internal/security/analyzer/skill_analyzer.go
func NewSkillAnalyzerAdapter(name string, cat security.Category, fn AnalyzeFunc) *SkillAnalyzerAdapter {
return &SkillAnalyzerAdapter{
name: name,
category: cat,
fn: fn,
}
}security.confidenceRank function · go · L69-L80 (12 LOC)internal/security/correlator.go
func confidenceRank(c Confidence) int {
switch c {
case ConfidenceHigh:
return 3
case ConfidenceMedium:
return 2
case ConfidenceLow:
return 1
default:
return 0
}
}security.Correlator.Correlate method · go · L84-L93 (10 LOC)internal/security/correlator.go
func (c *Correlator) Correlate(findings []Finding) ([]AttackChain, []Finding) {
if len(findings) == 0 {
return nil, nil
}
deduped := c.deduplicate(findings)
chains := c.detectChains(deduped)
return chains, deduped
}security.Correlator.deduplicate method · go · L97-L127 (31 LOC)internal/security/correlator.go
func (c *Correlator) deduplicate(findings []Finding) []Finding {
best := make(map[dedupeKey]int) // key -> index into findings
for i, f := range findings {
key := dedupeKey{
CWE: f.CWE,
File: f.Location.File,
StartLine: f.Location.StartLine,
}
if existing, ok := best[key]; ok {
if confidenceRank(f.Confidence) > confidenceRank(findings[existing].Confidence) {
best[key] = i
}
} else {
best[key] = i
}
}
// Preserve original order for deterministic output.
seen := make(map[int]bool, len(best))
for _, idx := range best {
seen[idx] = true
}
result := make([]Finding, 0, len(best))
for i := range findings {
if seen[i] {
result = append(result, findings[i])
}
}
return result
}security.proximate function · go · L136-L153 (18 LOC)internal/security/correlator.go
func proximate(a, b Finding, sameFunc bool) bool {
if a.Location.File != b.Location.File {
return false
}
if sameFunc {
// Prefer function-name match when both are available.
if a.Location.Function != "" && b.Location.Function != "" {
return a.Location.Function == b.Location.Function
}
// Fall back to line proximity when function names are unavailable
// (e.g., findings from static scanners that don't use tree-sitter).
return linesClose(a.Location, b.Location, proximityThreshold)
}
// Same file is sufficient for non-sameFunc patterns.
return true
}Repobility · code-quality intelligence · https://repobility.com
security.linesClose function · go · L157-L172 (16 LOC)internal/security/correlator.go
func linesClose(a, b Location, threshold int) bool {
// Need valid line numbers to compare.
if a.StartLine == 0 || b.StartLine == 0 {
return false
}
aEnd := a.EndLine
if aEnd == 0 {
aEnd = a.StartLine
}
bEnd := b.EndLine
if bEnd == 0 {
bEnd = b.StartLine
}
// Check if ranges overlap or are within threshold.
return a.StartLine <= bEnd+threshold && b.StartLine <= aEnd+threshold
}security.Correlator.detectChains method · go · L175-L225 (51 LOC)internal/security/correlator.go
func (c *Correlator) detectChains(findings []Finding) []AttackChain {
var chains []AttackChain
chainID := 0
// Track which finding pairs have already been used in chains to avoid duplicates.
used := make(map[string]bool)
for _, pattern := range knownPatterns {
for i := 0; i < len(findings); i++ {
for j := i + 1; j < len(findings); j++ {
fi, fj := findings[i], findings[j]
// Check both orderings of the pattern categories.
matched := false
if fi.Category == pattern.cat1 && fj.Category == pattern.cat2 {
matched = true
} else if fi.Category == pattern.cat2 && fj.Category == pattern.cat1 {
matched = true
}
if !matched {
continue
}
if !proximate(fi, fj, pattern.sameFunc) {
continue
}
// Build a unique key for this pair to prevent duplicate chains.
pairKey := fmt.Sprintf("%s:%s:%s", pattern.title, fi.ID, fj.ID)
if used[pairKey] {
continue
}
used[pairKey] = true
chainID++
chain := Attasecurity.NewEngine function · go · L21-L26 (6 LOC)internal/security/engine.go
func NewEngine(config EngineConfig) *Engine {
if config.Concurrency <= 0 {
config.Concurrency = 1
}
return &Engine{config: config}
}security.Engine.Run method · go · L44-L109 (66 LOC)internal/security/engine.go
func (e *Engine) Run(ctx context.Context, target ScanTarget) (*Report, error) {
start := time.Now()
if err := ctx.Err(); err != nil {
return nil, fmt.Errorf("engine cancelled before start: %w", err)
}
// Merge engine-level exclude patterns into the target.
mergedTarget := target
if len(e.config.ExcludePatterns) > 0 {
mergedTarget.ExcludePatterns = append(
append([]string{}, target.ExcludePatterns...),
e.config.ExcludePatterns...,
)
}
// Phase 1: static scanners.
staticFindings, scanErrors := e.runScanners(ctx, mergedTarget)
// Check context between phases.
if err := ctx.Err(); err != nil {
return nil, fmt.Errorf("engine cancelled after phase 1: %w", err)
}
// Prioritize code segments for LLM analysis.
prioritizer := NewPrioritizer(PrioritizerConfig{
MinRiskScore: e.config.MinRiskScore,
MaxChunks: e.config.MaxLLMChunks,
})
chunks, err := prioritizer.Prioritize(ctx, mergedTarget, staticFindings)
if err != nil {
return nil, fmt.Errorf("prioritsecurity.Engine.runScanners method · go · L113-L143 (31 LOC)internal/security/engine.go
func (e *Engine) runScanners(ctx context.Context, target ScanTarget) ([]Finding, []ScanError) {
if len(e.scanners) == 0 {
return nil, nil
}
p := pool.New().WithMaxGoroutines(e.config.Concurrency)
var mu sync.Mutex
var findings []Finding
var errors []ScanError
for _, s := range e.scanners {
s := s // capture loop variable
p.Go(func() {
result, err := s.Scan(ctx, target)
mu.Lock()
defer mu.Unlock()
if err != nil {
errors = append(errors, ScanError{
Scanner: s.Name(),
Err: err,
Fatal: false,
})
return
}
findings = append(findings, result...)
})
}
p.Wait()
return findings, errors
}security.Engine.runAnalyzers method · go · L147-L177 (31 LOC)internal/security/engine.go
func (e *Engine) runAnalyzers(ctx context.Context, chunks []AnalysisChunk) ([]Finding, []ScanError) {
if len(e.analyzers) == 0 {
return nil, nil
}
p := pool.New().WithMaxGoroutines(e.config.Concurrency)
var mu sync.Mutex
var findings []Finding
var errors []ScanError
for _, a := range e.analyzers {
a := a // capture loop variable
p.Go(func() {
result, err := a.Analyze(ctx, chunks)
mu.Lock()
defer mu.Unlock()
if err != nil {
errors = append(errors, ScanError{
Scanner: a.Name(),
Err: err,
Fatal: false,
})
return
}
findings = append(findings, result...)
})
}
p.Wait()
return findings, errors
}security.countFiles function · go · L180-L189 (10 LOC)internal/security/engine.go
func countFiles(target ScanTarget) int {
if len(target.Files) > 0 {
return len(target.Files)
}
files, err := CollectFiles(target, nil)
if err != nil {
return 0
}
return len(files)
}security.CollectFiles function · go · L12-L56 (45 LOC)internal/security/fileutil.go
func CollectFiles(target ScanTarget, extensions []string) ([]string, error) {
if len(target.Files) > 0 {
if len(extensions) == 0 {
return target.Files, nil
}
extSet := toExtSet(extensions)
var filtered []string
for _, f := range target.Files {
if extSet[filepath.Ext(f)] {
filtered = append(filtered, f)
}
}
return filtered, nil
}
extSet := toExtSet(extensions)
var files []string
err := filepath.Walk(target.RootDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if info.IsDir() {
return nil
}
relPath, err := filepath.Rel(target.RootDir, path)
if err != nil {
return nil
}
if IsExcluded(relPath, target.ExcludePatterns) {
return nil
}
if len(extSet) > 0 {
if !extSet[filepath.Ext(relPath)] {
return nil
}
}
files = append(files, relPath)
return nil
})
return files, err
}Repobility — same analyzer, your code, free for public repos · /scan/
security.toExtSet function · go · L76-L85 (10 LOC)internal/security/fileutil.go
func toExtSet(extensions []string) map[string]bool {
if len(extensions) == 0 {
return nil
}
set := make(map[string]bool, len(extensions))
for _, ext := range extensions {
set[ext] = true
}
return set
}output.CycloneDXFormatter.Format method · go · L66-L80 (15 LOC)internal/security/output/cyclonedx.go
func (f *CycloneDXFormatter) Format(report *security.Report) ([]byte, error) {
bom := cdxBOM{
BOMFormat: "CycloneDX",
SpecVersion: "1.5",
Version: 1,
Metadata: cdxMetadata{
Tools: []cdxTool{
{Name: "rubichan", Version: "0.1.0"},
},
},
Vulnerabilities: buildVulnerabilities(report.Findings),
}
return json.MarshalIndent(bom, "", " ")
}output.parseCWE function · go · L84-L93 (10 LOC)internal/security/output/cyclonedx.go
func parseCWE(cwe string) int {
if !strings.HasPrefix(cwe, "CWE-") {
return -1
}
num, err := strconv.Atoi(cwe[4:])
if err != nil {
return -1
}
return num
}output.buildVulnerabilities function · go · L96-L123 (28 LOC)internal/security/output/cyclonedx.go
func buildVulnerabilities(findings []security.Finding) []cdxVulnerability {
vulns := make([]cdxVulnerability, 0, len(findings))
for _, f := range findings {
vuln := cdxVulnerability{
ID: f.ID,
Description: f.Title,
Source: cdxSource{Name: "rubichan"},
Ratings: []cdxRating{
{
Severity: string(f.Severity),
Method: "other",
},
},
Affects: []cdxAffect{
{Ref: f.Location.File},
},
}
if cweNum := parseCWE(f.CWE); cweNum > 0 {
vuln.CWEs = []int{cweNum}
}
vulns = append(vulns, vuln)
}
return vulns
}output.severityEmoji function · go · L39-L52 (14 LOC)internal/security/output/github_pr.go
func severityEmoji(s security.Severity) string {
switch s {
case security.SeverityCritical:
return "\U0001f6a8" // rotating light
case security.SeverityHigh:
return "\u274c" // cross mark
case security.SeverityMedium:
return "\u26a0\ufe0f" // warning
case security.SeverityLow:
return "\u2139\ufe0f" // information
default:
return "\U0001f4ac" // speech bubble
}
}output.GitHubPRFormatter.Format method · go · L55-L62 (8 LOC)internal/security/output/github_pr.go
func (f *GitHubPRFormatter) Format(report *security.Report) ([]byte, error) {
review := PRReview{
Body: buildSummaryBody(report),
Comments: buildComments(report.Findings),
}
return json.MarshalIndent(review, "", " ")
}output.buildSummaryBody function · go · L65-L100 (36 LOC)internal/security/output/github_pr.go
func buildSummaryBody(report *security.Report) string {
summary := report.Summary()
var b strings.Builder
b.WriteString(fmt.Sprintf("## Security Scan: %d findings\n\n", summary.Total))
if summary.Total == 0 {
b.WriteString("No security findings detected.\n")
return b.String()
}
b.WriteString("| Severity | Count |\n")
b.WriteString("|----------|-------|\n")
if summary.Critical > 0 {
b.WriteString(fmt.Sprintf("| %s critical | %d |\n", severityEmoji(security.SeverityCritical), summary.Critical))
}
if summary.High > 0 {
b.WriteString(fmt.Sprintf("| %s high | %d |\n", severityEmoji(security.SeverityHigh), summary.High))
}
if summary.Medium > 0 {
b.WriteString(fmt.Sprintf("| %s medium | %d |\n", severityEmoji(security.SeverityMedium), summary.Medium))
}
if summary.Low > 0 {
b.WriteString(fmt.Sprintf("| %s low | %d |\n", severityEmoji(security.SeverityLow), summary.Low))
}
if summary.Info > 0 {
b.WriteString(fmt.Sprintf("| %s info | %d |\n", severityEmoji(secoutput.buildComments function · go · L103-L132 (30 LOC)internal/security/output/github_pr.go
func buildComments(findings []security.Finding) []PRComment {
comments := make([]PRComment, 0, len(findings))
for _, f := range findings {
var body strings.Builder
body.WriteString(fmt.Sprintf("%s **[%s] %s**\n\n", severityEmoji(f.Severity), string(f.Severity), f.Title))
if f.Description != "" {
body.WriteString(f.Description)
body.WriteString("\n\n")
}
if f.CWE != "" {
body.WriteString(fmt.Sprintf("**CWE:** %s\n", f.CWE))
}
if f.Remediation != "" {
body.WriteString(fmt.Sprintf("\n**Remediation:** %s\n", f.Remediation))
}
comments = append(comments, PRComment{
Path: f.Location.File,
Line: f.Location.StartLine,
Body: body.String(),
Severity: string(f.Severity),
})
}
return comments
}Powered by Repobility — scan your code at https://repobility.com
output.JSONFormatter.Format method · go · L86-L99 (14 LOC)internal/security/output/json.go
func (f *JSONFormatter) Format(report *security.Report) ([]byte, error) {
jr := jsonReport{
Findings: convertFindings(report.Findings),
AttackChains: convertChains(report.AttackChains),
Summary: convertSummary(report.Summary()),
Stats: jsonStats{
DurationMS: report.Stats.Duration.Milliseconds(),
FilesScanned: report.Stats.FilesScanned,
ChunksAnalyzed: report.Stats.ChunksAnalyzed,
},
}
return json.MarshalIndent(jr, "", " ")
}output.convertFindings function · go · L102-L111 (10 LOC)internal/security/output/json.go
func convertFindings(findings []security.Finding) []jsonFinding {
if findings == nil {
return []jsonFinding{}
}
result := make([]jsonFinding, len(findings))
for i, f := range findings {
result[i] = convertFinding(f)
}
return result
}output.convertFinding function · go · L114-L141 (28 LOC)internal/security/output/json.go
func convertFinding(f security.Finding) jsonFinding {
refs := f.References
if refs == nil {
refs = []string{}
}
return jsonFinding{
ID: f.ID,
Scanner: f.Scanner,
Severity: string(f.Severity),
Category: string(f.Category),
Title: f.Title,
Description: f.Description,
Location: jsonLocation{
File: f.Location.File,
StartLine: f.Location.StartLine,
EndLine: f.Location.EndLine,
Function: f.Location.Function,
},
CWE: f.CWE,
OWASP: f.OWASP,
Evidence: f.Evidence,
Remediation: f.Remediation,
Confidence: string(f.Confidence),
References: refs,
Metadata: f.Metadata,
SkillSource: f.SkillSource,
}
}output.convertChains function · go · L144-L160 (17 LOC)internal/security/output/json.go
func convertChains(chains []security.AttackChain) []jsonAttackChain {
if chains == nil {
return []jsonAttackChain{}
}
result := make([]jsonAttackChain, len(chains))
for i, c := range chains {
result[i] = jsonAttackChain{
ID: c.ID,
Title: c.Title,
Severity: string(c.Severity),
Steps: convertFindings(c.Steps),
Impact: c.Impact,
Likelihood: c.Likelihood,
}
}
return result
}output.convertSummary function · go · L163-L173 (11 LOC)internal/security/output/json.go
func convertSummary(s security.ReportSummary) jsonSummary {
return jsonSummary{
Critical: s.Critical,
High: s.High,
Medium: s.Medium,
Low: s.Low,
Info: s.Info,
Chains: s.Chains,
Total: s.Total,
}
}output.severityLabel function · go · L33-L48 (16 LOC)internal/security/output/markdown.go
func severityLabel(s security.Severity) string {
switch s {
case security.SeverityCritical:
return "Critical"
case security.SeverityHigh:
return "High"
case security.SeverityMedium:
return "Medium"
case security.SeverityLow:
return "Low"
case security.SeverityInfo:
return "Info"
default:
return string(s)
}
}output.MarkdownFormatter.Format method · go · L51-L101 (51 LOC)internal/security/output/markdown.go
func (f *MarkdownFormatter) Format(report *security.Report) ([]byte, error) {
var b strings.Builder
summary := report.Summary()
// Header
b.WriteString("# Security Scan Report\n\n")
// Summary table
b.WriteString("## Summary\n\n")
b.WriteString("| Severity | Count |\n")
b.WriteString("|----------|-------|\n")
b.WriteString(fmt.Sprintf("| Critical | %d |\n", summary.Critical))
b.WriteString(fmt.Sprintf("| High | %d |\n", summary.High))
b.WriteString(fmt.Sprintf("| Medium | %d |\n", summary.Medium))
b.WriteString(fmt.Sprintf("| Low | %d |\n", summary.Low))
b.WriteString(fmt.Sprintf("| Info | %d |\n", summary.Info))
b.WriteString("\n")
b.WriteString(fmt.Sprintf("**Total findings:** %d | **Attack chains:** %d | **Duration:** %dms\n\n",
summary.Total, summary.Chains, report.Stats.Duration.Milliseconds()))
// Group findings by severity
bySeverity := make(map[security.Severity][]security.Finding)
for _, finding := range report.Findings {
bySeverity[finding.Severity] output.formatLocation function · go · L104-L113 (10 LOC)internal/security/output/markdown.go
func formatLocation(loc security.Location) string {
s := fmt.Sprintf("%s:%d", loc.File, loc.StartLine)
if loc.EndLine > loc.StartLine {
s = fmt.Sprintf("%s:%d-%d", loc.File, loc.StartLine, loc.EndLine)
}
if loc.Function != "" {
s += fmt.Sprintf(" (%s)", loc.Function)
}
return s
}Repobility · MCP-ready · https://repobility.com
output.writeFinding function · go · L116-L129 (14 LOC)internal/security/output/markdown.go
func writeFinding(b *strings.Builder, f security.Finding) {
b.WriteString(fmt.Sprintf("### [%s] %s\n\n", f.ID, f.Title))
b.WriteString(fmt.Sprintf("- **Severity:** %s | **Confidence:** %s\n",
severityLabel(f.Severity), severityLabel(security.Severity(f.Confidence))))
b.WriteString(fmt.Sprintf("- **Location:** %s\n", formatLocation(f.Location)))
b.WriteString(fmt.Sprintf("- **Category:** %s | **CWE:** %s\n", string(f.Category), f.CWE))
if f.Description != "" {
b.WriteString(fmt.Sprintf("- **Description:** %s\n", f.Description))
}
if f.Remediation != "" {
b.WriteString(fmt.Sprintf("- **Remediation:** %s\n", f.Remediation))
}
b.WriteString("\n")
}output.writeChain function · go · L132-L149 (18 LOC)internal/security/output/markdown.go
func writeChain(b *strings.Builder, c security.AttackChain) {
b.WriteString(fmt.Sprintf("### [%s] %s\n\n", c.ID, c.Title))
b.WriteString(fmt.Sprintf("- **Severity:** %s\n", severityLabel(c.Severity)))
if c.Impact != "" {
b.WriteString(fmt.Sprintf("- **Impact:** %s\n", c.Impact))
}
if len(c.Steps) > 0 {
b.WriteString("- **Steps:**\n")
for i, step := range c.Steps {
loc := step.Location.File
if step.Location.StartLine > 0 {
loc = fmt.Sprintf("%s:%d", step.Location.File, step.Location.StartLine)
}
b.WriteString(fmt.Sprintf(" %d. [%s] %s (%s)\n", i+1, step.ID, step.Title, loc))
}
}
b.WriteString("\n")
}output.SARIFFormatter.Format method · go · L82-L104 (23 LOC)internal/security/output/sarif.go
func (f *SARIFFormatter) Format(report *security.Report) ([]byte, error) {
rules := buildRules(report.Findings)
results := buildResults(report.Findings)
doc := sarifDocument{
Schema: sarifSchemaURL,
Version: "2.1.0",
Runs: []sarifRun{
{
Tool: sarifTool{
Driver: sarifDriver{
Name: "rubichan",
Version: "0.1.0",
Rules: rules,
},
},
Results: results,
},
},
}
return json.MarshalIndent(doc, "", " ")
}