Skip to content

Commit bc93694

Browse files
bhvkshahbsharifi
authored andcommitted
Security improvements.
1 parent 2e36f69 commit bc93694

File tree

1 file changed

+70
-43
lines changed

1 file changed

+70
-43
lines changed

src/main/java/com/amazon/redshift/core/v3/SimpleParameterList.java

+70-43
Original file line numberDiff line numberDiff line change
@@ -184,103 +184,130 @@ public void setNull(int index, int oid) throws SQLException {
184184
bind(index, NULL_OBJECT, oid, binaryTransfer);
185185
}
186186

187+
private static String quoteAndCast(String text, String type, boolean standardConformingStrings)
188+
{
189+
StringBuilder sb = new StringBuilder((text.length() + 10) / 10 * 11); // Add 10% for escaping.
190+
sb.append("('");
191+
try {
192+
Utils.escapeLiteral(sb, text, standardConformingStrings);
193+
} catch (SQLException e) {
194+
// This should only happen if we have an embedded null
195+
// and there's not much we can do if we do hit one.
196+
//
197+
// To force a server side failure, we deliberately include
198+
// a zero byte character in the literal to force the server
199+
// to reject the command.
200+
sb.append('\u0000');
201+
}
202+
sb.append("'");
203+
if (type != null) {
204+
sb.append("::");
205+
sb.append(type);
206+
}
207+
sb.append(")");
208+
return sb.toString();
209+
}
210+
187211
@Override
188212
public String toString(int index, boolean standardConformingStrings) {
189213
--index;
190214
if (paramValues[index] == null) {
191215
return "?";
192216
} else if (paramValues[index] == NULL_OBJECT) {
193217
return "NULL";
194-
} else if ((flags[index] & BINARY) == BINARY) {
218+
}
219+
String textValue;
220+
String type;
221+
if ((flags[index] & BINARY) == BINARY) {
195222
// handle some of the numeric types
196-
197223
switch (paramTypes[index]) {
198224
case Oid.INT2:
199225
short s = ByteConverter.int2((byte[]) paramValues[index], 0);
200-
return Short.toString(s);
226+
textValue = Short.toString(s);
227+
type = "int2";
228+
break;
201229

202230
case Oid.INT4:
203231
int i = ByteConverter.int4((byte[]) paramValues[index], 0);
204-
return Integer.toString(i);
232+
textValue = Integer.toString(i);
233+
type = "int4";
234+
break;
205235

206236
case Oid.INT8:
207237
long l = ByteConverter.int8((byte[]) paramValues[index], 0);
208-
return Long.toString(l);
238+
textValue = Long.toString(l);
239+
type = "int8";
240+
break;
209241

210242
case Oid.FLOAT4:
211243
float f = ByteConverter.float4((byte[]) paramValues[index], 0);
212244
if (Float.isNaN(f)) {
213245
return "'NaN'::real";
214246
}
215-
return Float.toString(f);
247+
textValue = Float.toString(f);
248+
type = "real";
249+
break;
216250

217251
case Oid.FLOAT8:
218252
double d = ByteConverter.float8((byte[]) paramValues[index], 0);
219253
if (Double.isNaN(d)) {
220254
return "'NaN'::double precision";
221255
}
222-
return Double.toString(d);
256+
textValue = Double.toString(d);
257+
type = "double precision";
258+
break;
223259

224260
case Oid.UUID:
225-
String uuid =
261+
textValue =
226262
new UUIDArrayAssistant().buildElement((byte[]) paramValues[index], 0, 16).toString();
227-
return "'" + uuid + "'::uuid";
263+
type = "uuid";
264+
break;
228265

229266
case Oid.POINT:
230267
RedshiftPoint pgPoint = new RedshiftPoint();
231268
pgPoint.setByteValue((byte[]) paramValues[index], 0);
232-
return "'" + pgPoint.toString() + "'::point";
269+
textValue = pgPoint.toString();
270+
type = "point";
271+
break;
233272

234273
case Oid.BOX:
235274
RedshiftBox pgBox = new RedshiftBox();
236275
pgBox.setByteValue((byte[]) paramValues[index], 0);
237-
return "'" + pgBox.toString() + "'::box";
276+
textValue = pgBox.toString();
277+
type = "box";
278+
break;
279+
280+
default:
281+
return "?";
238282
}
239-
return "?";
240283
} else {
241-
String param = paramValues[index].toString();
242-
243-
// add room for quotes + potential escaping.
244-
StringBuilder p = new StringBuilder(3 + (param.length() + 10) / 10 * 11);
245-
246-
// No E'..' here since escapeLiteral escapes all things and it does not use \123 kind of
247-
// escape codes
248-
p.append('\'');
249-
try {
250-
p = Utils.escapeLiteral(p, param, standardConformingStrings);
251-
} catch (SQLException sqle) {
252-
// This should only happen if we have an embedded null
253-
// and there's not much we can do if we do hit one.
254-
//
255-
// The goal of toString isn't to be sent to the server,
256-
// so we aren't 100% accurate (see StreamWrapper), put
257-
// the unescaped version of the data.
258-
//
259-
p.append(param);
260-
}
261-
p.append('\'');
284+
textValue = paramValues[index].toString();
262285
int paramType = paramTypes[index];
286+
263287
if (paramType == Oid.TIMESTAMP) {
264-
p.append("::timestamp");
288+
type = "timestamp";
265289
} else if (paramType == Oid.TIMESTAMPTZ) {
266-
p.append("::timestamp with time zone");
290+
type = "timestamp with time zone";
267291
} else if (paramType == Oid.TIME) {
268-
p.append("::time");
292+
type = "time";
269293
} else if (paramType == Oid.TIMETZ) {
270-
p.append("::time with time zone");
294+
type = "time with time zone";
271295
} else if (paramType == Oid.DATE) {
272-
p.append("::date");
296+
type = "date";
273297
} else if (paramType == Oid.INTERVAL) {
274-
p.append("::interval");
298+
type = "interval";
275299
} else if (paramType == Oid.INTERVALY2M) {
276-
p.append("::interval year to month");
300+
type = "interval year to month";
277301
} else if (paramType == Oid.INTERVALD2S) {
278-
p.append("::interval day to second");
302+
type = "interval day to second";
279303
} else if (paramType == Oid.NUMERIC) {
280-
p.append("::numeric");
304+
type = "numeric";
305+
}
306+
else {
307+
type = null;
281308
}
282-
return p.toString();
283309
}
310+
return quoteAndCast(textValue, type, standardConformingStrings);
284311
}
285312

286313
@Override

0 commit comments

Comments
 (0)