{"record":{"id":"dd752a4abbce77ee","repo":"gastownhall/beads","slug":"invalid-fromref-w","errorCode":null,"errorMessage":"invalid fromRef: %w","messagePattern":"invalid fromRef: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/diff.go","lineNumber":18,"sourceCode":"package issueops\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\n\t\"github.com/steveyegge/beads/internal/storage\"\n\t\"github.com/steveyegge/beads/internal/types\"\n)\n\n// DiffInTx returns changes between two commits or branches by querying\n// Dolt's dolt_diff() table function.\n//\n// nolint:gosec // G201: refs are validated by ValidateRef() - dolt_diff requires literal refs\nfunc DiffInTx(ctx context.Context, tx *sql.Tx, fromRef, toRef string) ([]*storage.DiffEntry, error) {\n\tif err := ValidateRef(fromRef); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid fromRef: %w\", err)\n\t}\n\tif err := ValidateRef(toRef); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid toRef: %w\", err)\n\t}\n\n\tquery := fmt.Sprintf(`\n\t\tSELECT\n\t\t\tCOALESCE(from_id, '') as from_id,\n\t\t\tCOALESCE(to_id, '') as to_id,\n\t\t\tdiff_type,\n\t\t\tfrom_title, to_title,\n\t\t\tfrom_description, to_description,\n\t\t\tfrom_status, to_status,\n\t\t\tfrom_priority, to_priority\n\t\tFROM dolt_diff('%s', '%s', 'issues')\n\t`, fromRef, toRef)\n\n\trows, err := tx.QueryContext(ctx, query)","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/diff.go#L1-L36","documentation":"DiffInTx validates both refs with ValidateRef before interpolating them into a dolt_diff() table-function call; this error is raised when fromRef fails validation. The library rejects refs containing SQL metacharacters or invalid forms because dolt_diff requires literal ref names. It is a caller-input error, not a database failure.","triggerScenarios":"Calling DiffInTx with a fromRef that is empty, contains quotes/semicolons, or otherwise fails ValidateRef (e.g. \"main'; DROP TABLE issues--\", a ref with illegal characters, or a non-ref string).","commonSituations":"Passing raw branch names containing shell-style characters; building refs from untrusted CLI input; typos like trailing slashes or spaces; passing a refspec form the validator rejects.","solutions":["Inspect the wrapped ValidateRef error to see which rule the ref violated","Use plain branch/tag/commit names without quotes or special characters as fromRef","Validate user-supplied refs at your CLI boundary before calling DiffInTx","Pre-check with ValidateRef yourself to fail fast with a clearer message"],"exampleFix":"// before\nDiffInTx(ctx, tx, \"main'; --\", \"feature\")\n// after\nfrom := \"main\" // plain, validated ref\nif err := issueops.ValidateRef(from); err != nil {\n    return fmt.Errorf(\"bad from ref %q: %w\", from, err)\n}\nDiffInTx(ctx, tx, from, \"feature\")","handlingStrategy":"validation","validationCode":"func validRef(ref string) bool {\n    if ref == \"\" || len(ref) > 200 {\n        return false\n    }\n    for _, r := range ref {\n        if !('a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || '0' <= r && r <= '9' ||\n            r == '-' || r == '_' || r == '.' || r == '/') {\n            return false\n        }\n    }\n    return true\n}\n// call ValidateRef(from) yourself before DiffInTx to fail fast","typeGuard":null,"tryCatchPattern":"if err := DiffInTx(ctx, tx, from, to); err != nil {\n    if strings.Contains(err.Error(), \"invalid fromRef\") {\n        return fmt.Errorf(\"usage: from must be a plain branch/tag/commit name, got %q\", from)\n    }\n    return err\n}","preventionTips":["Always pre-validate user-supplied refs with ValidateRef","Never pass refs containing quotes, semicolons, or whitespace","Normalize branch names before calling (trim spaces, reject ranges)","Keep ref input parsing at one boundary in your CLI"],"tags":["validation","sql-injection","refs","input"],"backgroundTag":"invalid-ref","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}