Skip to content

Commit 2a417c1

Browse files
authored
Merge pull request #61 from SpecterOps/seanj/BED-7946
fix(pg): escape quoting within formatted string values
2 parents 4b82ee3 + b263798 commit 2a417c1

3 files changed

Lines changed: 31 additions & 1 deletion

File tree

cypher/models/pgsql/format/format.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func formatValue(builder *OutputBuilder, value any) error {
116116
return formatSlice(builder, typedValue, pgsql.Int8Array)
117117

118118
case string:
119-
builder.Write("'", typedValue, "'")
119+
builder.Write("'", strings.ReplaceAll(typedValue, "'", "''"), "'")
120120

121121
case bool:
122122
builder.Write(strconv.FormatBool(typedValue))

cypher/models/pgsql/format/format_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,3 +571,31 @@ func TestFormat_CTEs(t *testing.T) {
571571
require.Nil(t, err)
572572
require.Equal(t, "with recursive expansion_1(root_id, next_id, depth, stop, is_cycle, path) as materialized (select r.start_id, r.end_id, 1, false, r.start_id = r.end_id, array [r.id] from edge r join node a on a.id = r.start_id where a.kind_ids operator (pg_catalog.&&) array [23]::int2[] union all select expansion_1.root_id, r.end_id, expansion_1.depth + 1, b.kind_ids operator (pg_catalog.&&) array [24]::int2[], r.id = any(expansion_1.path), expansion_1.path || r.id from expansion_1 join edge r on r.start_id = expansion_1.next_id join node b on b.id = r.end_id where not expansion_1.is_cycle and not expansion_1.stop) select a.properties, b.properties from expansion_1 join node a on a.id = expansion_1.root_id join node b on b.id = expansion_1.next_id where not expansion_1.is_cycle and expansion_1.stop;", formattedQuery)
573573
}
574+
575+
func TestFormat_QueryInjection(t *testing.T) {
576+
query := pgsql.Query{
577+
Body: pgsql.Select{
578+
Distinct: false,
579+
Projection: []pgsql.SelectItem{
580+
pgsql.Wildcard{},
581+
},
582+
From: []pgsql.FromClause{{
583+
Source: pgsql.TableReference{
584+
Name: pgsql.CompoundIdentifier{"table"},
585+
Binding: pgsql.AsOptionalIdentifier("t"),
586+
},
587+
}},
588+
Where: pgsql.BinaryExpression{
589+
LOperand: pgsql.CompoundIdentifier{"t", "col1"},
590+
Operator: pgsql.Operator("="),
591+
ROperand: pgsql.Literal{
592+
Value: "alpha' || select ('malicious')",
593+
},
594+
},
595+
},
596+
}
597+
598+
formattedQuery, err := format.Statement(query, format.NewOutputBuilder())
599+
require.Nil(t, err)
600+
require.Equal(t, `select * from table t where t.col1 = 'alpha'' || select (''malicious'')';`, formattedQuery)
601+
}

cypher/models/pgsql/test/translation_cases/nodes.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,5 @@ with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from
312312
-- case: match (n:NodeKind1) optional match (m:NodeKind2) where m.distinguishedname = n.unknown + m.unknown optional match (o:NodeKind2) where o.distinguishedname <> n.otherunknown return n, m, o
313313
with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where n0.kind_ids operator (pg_catalog.@>) array [1]::int2[]), s1 as (select s0.n0 as n0, (n1.id, n1.kind_ids, n1.properties)::nodecomposite as n1 from s0, node n1 where ((n1.properties ->> 'distinguishedname') = ((s0.n0).properties -> 'unknown') + (n1.properties -> 'unknown')) and n1.kind_ids operator (pg_catalog.@>) array [2]::int2[]), s2 as (select s0.n0 as n0, s1.n1 as n1 from s0 left outer join s1 on (s0.n0 = s1.n0)), s3 as (select s2.n0 as n0, s2.n1 as n1, (n2.id, n2.kind_ids, n2.properties)::nodecomposite as n2 from s2, node n2 where ((n2.properties -> 'distinguishedname') <> ((s2.n0).properties -> 'otherunknown')) and n2.kind_ids operator (pg_catalog.@>) array [2]::int2[]), s4 as (select s2.n0 as n0, s2.n1 as n1, s3.n2 as n2 from s2 left outer join s3 on (s2.n1 = s3.n1) and (s2.n0 = s3.n0)) select s4.n0 as n, s4.n1 as m, s4.n2 as o from s4;
314314

315+
-- case: match (n) where n.name = "alpha' || (SELECT inet_server_addr()::text::int) || '" return n
316+
with s0 as (select (n0.id, n0.kind_ids, n0.properties)::nodecomposite as n0 from node n0 where ((n0.properties ->> 'name') = 'alpha'' || (SELECT inet_server_addr()::text::int) || ''')) select s0.n0 as n from s0;

0 commit comments

Comments
 (0)