← back to julianshen__rubichan

Function bodies 513 total

All specs Real LLM only Function bodies
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

		// Handle
provider.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
	}

	re
analyzer.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 := Atta
security.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("priorit
security.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(sec
output.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, "", "  ")
}
‹ prevpage 3 / 11next ›