Skip to content

Commit 04d2b62

Browse files
committed
fully address pb33f/wiretap#134
there have been a lot more nil checks added, but this really goes deep, so I can close out this issue.
1 parent 881f8cb commit 04d2b62

13 files changed

+841
-129
lines changed

errors/parameter_errors.go

Lines changed: 151 additions & 86 deletions
Large diffs are not rendered by default.

errors/parameter_errors_test.go

Lines changed: 314 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley
1+
// Copyright 2023-2026 Princess Beef Heavy Industries, LLC / Dave Shanley
22
// https://pb33f.io
33

44
package errors
@@ -1242,3 +1242,316 @@ items:
12421242
require.Contains(t, err.Reason, "The query parameter (which is an array) 'testQueryParam' contains the following duplicates: 'fish, cake'")
12431243
require.Contains(t, err.HowToFix, "Ensure the array values are all unique")
12441244
}
1245+
1246+
// createMinimalParameter creates a parameter with nil GoLow nodes to test nil safety.
1247+
func createMinimalParameter() *v3.Parameter {
1248+
param := &lowv3.Parameter{
1249+
Name: low.NodeReference[string]{Value: "minParam"},
1250+
// All node references intentionally left with nil KeyNode/ValueNode
1251+
}
1252+
return v3.NewParameter(param)
1253+
}
1254+
1255+
func TestParameterErrors_NilGoLowNodes(t *testing.T) {
1256+
// Tests that all parameter error constructors handle nil GoLow nodes
1257+
// without panicking. This covers the crash scenario from wiretap #134.
1258+
param := createMinimalParameter()
1259+
qp := &helpers.QueryParam{
1260+
Key: "minParam",
1261+
Values: []string{"value"},
1262+
}
1263+
sch := &base.Schema{}
1264+
1265+
t.Run("IncorrectFormEncoding", func(t *testing.T) {
1266+
err := IncorrectFormEncoding(param, qp, 0)
1267+
require.NotNil(t, err)
1268+
require.Equal(t, 1, err.SpecLine)
1269+
require.Equal(t, 0, err.SpecCol)
1270+
})
1271+
1272+
t.Run("IncorrectSpaceDelimiting", func(t *testing.T) {
1273+
err := IncorrectSpaceDelimiting(param, qp)
1274+
require.NotNil(t, err)
1275+
require.Equal(t, 1, err.SpecLine)
1276+
require.Equal(t, 0, err.SpecCol)
1277+
})
1278+
1279+
t.Run("IncorrectPipeDelimiting", func(t *testing.T) {
1280+
err := IncorrectPipeDelimiting(param, qp)
1281+
require.NotNil(t, err)
1282+
require.Equal(t, 1, err.SpecLine)
1283+
require.Equal(t, 0, err.SpecCol)
1284+
})
1285+
1286+
t.Run("InvalidDeepObject", func(t *testing.T) {
1287+
err := InvalidDeepObject(param, qp)
1288+
require.NotNil(t, err)
1289+
require.Equal(t, 1, err.SpecLine)
1290+
require.Equal(t, 0, err.SpecCol)
1291+
})
1292+
1293+
t.Run("QueryParameterMissing", func(t *testing.T) {
1294+
err := QueryParameterMissing(param, "/test", "get", "{}")
1295+
require.NotNil(t, err)
1296+
require.Equal(t, 1, err.SpecLine)
1297+
require.Equal(t, 0, err.SpecCol)
1298+
})
1299+
1300+
t.Run("HeaderParameterMissing", func(t *testing.T) {
1301+
err := HeaderParameterMissing(param, "/test", "get", "{}")
1302+
require.NotNil(t, err)
1303+
require.Equal(t, 1, err.SpecLine)
1304+
require.Equal(t, 0, err.SpecCol)
1305+
})
1306+
1307+
t.Run("CookieParameterMissing", func(t *testing.T) {
1308+
err := CookieParameterMissing(param, "/test", "get", "{}")
1309+
require.NotNil(t, err)
1310+
require.Equal(t, 1, err.SpecLine)
1311+
require.Equal(t, 0, err.SpecCol)
1312+
})
1313+
1314+
t.Run("HeaderParameterCannotBeDecoded", func(t *testing.T) {
1315+
err := HeaderParameterCannotBeDecoded(param, "bad", "/test", "get", "{}")
1316+
require.NotNil(t, err)
1317+
require.Equal(t, 1, err.SpecLine)
1318+
require.Equal(t, 0, err.SpecCol)
1319+
})
1320+
1321+
t.Run("IncorrectHeaderParamEnum", func(t *testing.T) {
1322+
err := IncorrectHeaderParamEnum(param, "bad", sch, "/test", "get", "{}")
1323+
require.NotNil(t, err)
1324+
require.Equal(t, 1, err.SpecLine)
1325+
require.Equal(t, 0, err.SpecCol)
1326+
})
1327+
1328+
t.Run("IncorrectQueryParamEnum", func(t *testing.T) {
1329+
err := IncorrectQueryParamEnum(param, "bad", sch, "/test", "get", "{}")
1330+
require.NotNil(t, err)
1331+
require.Equal(t, 1, err.SpecLine)
1332+
require.Equal(t, 0, err.SpecCol)
1333+
})
1334+
1335+
t.Run("IncorrectCookieParamEnum", func(t *testing.T) {
1336+
err := IncorrectCookieParamEnum(param, "bad", sch, "/test", "get", "{}")
1337+
require.NotNil(t, err)
1338+
require.Equal(t, 1, err.SpecLine)
1339+
require.Equal(t, 0, err.SpecCol)
1340+
})
1341+
1342+
t.Run("IncorrectPathParamEnum", func(t *testing.T) {
1343+
err := IncorrectPathParamEnum(param, "bad", sch, "/test", "{}")
1344+
require.NotNil(t, err)
1345+
require.Equal(t, 1, err.SpecLine)
1346+
require.Equal(t, 0, err.SpecCol)
1347+
})
1348+
1349+
t.Run("IncorrectQueryParamBool", func(t *testing.T) {
1350+
err := IncorrectQueryParamBool(param, "bad", sch, "/test", "get", "{}")
1351+
require.NotNil(t, err)
1352+
require.Equal(t, 1, err.SpecLine)
1353+
require.Equal(t, 0, err.SpecCol)
1354+
})
1355+
1356+
t.Run("InvalidQueryParamInteger", func(t *testing.T) {
1357+
err := InvalidQueryParamInteger(param, "bad", sch, "/test", "get", "{}")
1358+
require.NotNil(t, err)
1359+
require.Equal(t, 1, err.SpecLine)
1360+
require.Equal(t, 0, err.SpecCol)
1361+
})
1362+
1363+
t.Run("InvalidQueryParamNumber", func(t *testing.T) {
1364+
err := InvalidQueryParamNumber(param, "bad", sch, "/test", "get", "{}")
1365+
require.NotNil(t, err)
1366+
require.Equal(t, 1, err.SpecLine)
1367+
require.Equal(t, 0, err.SpecCol)
1368+
})
1369+
1370+
t.Run("IncorrectReservedValues", func(t *testing.T) {
1371+
err := IncorrectReservedValues(param, "a:b", sch, "/test", "get", "{}")
1372+
require.NotNil(t, err)
1373+
require.Equal(t, 1, err.SpecLine)
1374+
require.Equal(t, 0, err.SpecCol)
1375+
})
1376+
1377+
t.Run("InvalidHeaderParamInteger", func(t *testing.T) {
1378+
err := InvalidHeaderParamInteger(param, "bad", sch, "/test", "get", "{}")
1379+
require.NotNil(t, err)
1380+
require.Equal(t, 1, err.SpecLine)
1381+
require.Equal(t, 0, err.SpecCol)
1382+
})
1383+
1384+
t.Run("InvalidHeaderParamNumber", func(t *testing.T) {
1385+
err := InvalidHeaderParamNumber(param, "bad", sch, "/test", "get", "{}")
1386+
require.NotNil(t, err)
1387+
require.Equal(t, 1, err.SpecLine)
1388+
require.Equal(t, 0, err.SpecCol)
1389+
})
1390+
1391+
t.Run("InvalidCookieParamInteger", func(t *testing.T) {
1392+
err := InvalidCookieParamInteger(param, "bad", sch, "/test", "get", "{}")
1393+
require.NotNil(t, err)
1394+
require.Equal(t, 1, err.SpecLine)
1395+
require.Equal(t, 0, err.SpecCol)
1396+
})
1397+
1398+
t.Run("InvalidCookieParamNumber", func(t *testing.T) {
1399+
err := InvalidCookieParamNumber(param, "bad", sch, "/test", "get", "{}")
1400+
require.NotNil(t, err)
1401+
require.Equal(t, 1, err.SpecLine)
1402+
require.Equal(t, 0, err.SpecCol)
1403+
})
1404+
1405+
t.Run("IncorrectHeaderParamBool", func(t *testing.T) {
1406+
err := IncorrectHeaderParamBool(param, "bad", sch, "/test", "get", "{}")
1407+
require.NotNil(t, err)
1408+
require.Equal(t, 1, err.SpecLine)
1409+
require.Equal(t, 0, err.SpecCol)
1410+
})
1411+
1412+
t.Run("IncorrectCookieParamBool", func(t *testing.T) {
1413+
err := IncorrectCookieParamBool(param, "bad", sch, "/test", "get", "{}")
1414+
require.NotNil(t, err)
1415+
require.Equal(t, 1, err.SpecLine)
1416+
require.Equal(t, 0, err.SpecCol)
1417+
})
1418+
1419+
t.Run("IncorrectPathParamBool", func(t *testing.T) {
1420+
err := IncorrectPathParamBool(param, "bad", sch, "/test", "{}")
1421+
require.NotNil(t, err)
1422+
require.Equal(t, 1, err.SpecLine)
1423+
require.Equal(t, 0, err.SpecCol)
1424+
})
1425+
1426+
t.Run("IncorrectPathParamInteger", func(t *testing.T) {
1427+
err := IncorrectPathParamInteger(param, "bad", sch, "/test", "{}")
1428+
require.NotNil(t, err)
1429+
require.Equal(t, 1, err.SpecLine)
1430+
require.Equal(t, 0, err.SpecCol)
1431+
})
1432+
1433+
t.Run("IncorrectPathParamNumber", func(t *testing.T) {
1434+
err := IncorrectPathParamNumber(param, "bad", sch, "/test", "{}")
1435+
require.NotNil(t, err)
1436+
require.Equal(t, 1, err.SpecLine)
1437+
require.Equal(t, 0, err.SpecCol)
1438+
})
1439+
1440+
t.Run("IncorrectParamEncodingJSON", func(t *testing.T) {
1441+
err := IncorrectParamEncodingJSON(param, "bad", sch, "/test", "get", "{}")
1442+
require.NotNil(t, err)
1443+
require.Equal(t, 1, err.SpecLine)
1444+
require.Equal(t, 0, err.SpecCol)
1445+
})
1446+
1447+
t.Run("IncorrectQueryParamEnumArray", func(t *testing.T) {
1448+
err := IncorrectQueryParamEnumArray(param, "bad", sch, "/test", "get", "{}")
1449+
require.NotNil(t, err)
1450+
require.Equal(t, 1, err.SpecLine)
1451+
require.Equal(t, 0, err.SpecCol)
1452+
})
1453+
1454+
t.Run("PathParameterMissing", func(t *testing.T) {
1455+
err := PathParameterMissing(param, "/test/{id}", "/test/123")
1456+
require.NotNil(t, err)
1457+
require.Equal(t, 1, err.SpecLine)
1458+
require.Equal(t, 0, err.SpecCol)
1459+
})
1460+
}
1461+
1462+
func TestParameterErrors_NilSchemaItems(t *testing.T) {
1463+
// Tests array parameter error constructors with nil Items in schema.
1464+
param := createMinimalParameter()
1465+
sch := &base.Schema{} // no Items set
1466+
1467+
t.Run("IncorrectQueryParamArrayBoolean", func(t *testing.T) {
1468+
err := IncorrectQueryParamArrayBoolean(param, "bad", sch, sch, "/test", "get", "{}")
1469+
require.NotNil(t, err)
1470+
require.Equal(t, 1, err.SpecLine)
1471+
require.Equal(t, 0, err.SpecCol)
1472+
})
1473+
1474+
t.Run("IncorrectParamArrayMaxNumItems", func(t *testing.T) {
1475+
err := IncorrectParamArrayMaxNumItems(param, sch, 5, 10, "/test", "get", "{}")
1476+
require.NotNil(t, err)
1477+
require.Equal(t, 1, err.SpecLine)
1478+
require.Equal(t, 0, err.SpecCol)
1479+
})
1480+
1481+
t.Run("IncorrectParamArrayMinNumItems", func(t *testing.T) {
1482+
err := IncorrectParamArrayMinNumItems(param, sch, 5, 2, "/test", "get", "{}")
1483+
require.NotNil(t, err)
1484+
require.Equal(t, 1, err.SpecLine)
1485+
require.Equal(t, 0, err.SpecCol)
1486+
})
1487+
1488+
t.Run("IncorrectParamArrayUniqueItems", func(t *testing.T) {
1489+
err := IncorrectParamArrayUniqueItems(param, sch, "dup", "/test", "get", "{}")
1490+
require.NotNil(t, err)
1491+
require.Equal(t, 1, err.SpecLine)
1492+
require.Equal(t, 0, err.SpecCol)
1493+
})
1494+
1495+
t.Run("IncorrectCookieParamArrayBoolean", func(t *testing.T) {
1496+
err := IncorrectCookieParamArrayBoolean(param, "bad", sch, sch, "/test", "get", "{}")
1497+
require.NotNil(t, err)
1498+
require.Equal(t, 1, err.SpecLine)
1499+
require.Equal(t, 0, err.SpecCol)
1500+
})
1501+
1502+
t.Run("IncorrectQueryParamArrayInteger", func(t *testing.T) {
1503+
err := IncorrectQueryParamArrayInteger(param, "bad", sch, sch, "/test", "get", "{}")
1504+
require.NotNil(t, err)
1505+
require.Equal(t, 1, err.SpecLine)
1506+
require.Equal(t, 0, err.SpecCol)
1507+
})
1508+
1509+
t.Run("IncorrectQueryParamArrayNumber", func(t *testing.T) {
1510+
err := IncorrectQueryParamArrayNumber(param, "bad", sch, sch, "/test", "get", "{}")
1511+
require.NotNil(t, err)
1512+
require.Equal(t, 1, err.SpecLine)
1513+
require.Equal(t, 0, err.SpecCol)
1514+
})
1515+
1516+
t.Run("IncorrectCookieParamArrayNumber", func(t *testing.T) {
1517+
err := IncorrectCookieParamArrayNumber(param, "bad", sch, sch, "/test", "get", "{}")
1518+
require.NotNil(t, err)
1519+
require.Equal(t, 1, err.SpecLine)
1520+
require.Equal(t, 0, err.SpecCol)
1521+
})
1522+
1523+
t.Run("IncorrectHeaderParamArrayBoolean", func(t *testing.T) {
1524+
err := IncorrectHeaderParamArrayBoolean(param, "bad", sch, sch, "/test", "get", "{}")
1525+
require.NotNil(t, err)
1526+
require.Equal(t, 1, err.SpecLine)
1527+
require.Equal(t, 0, err.SpecCol)
1528+
})
1529+
1530+
t.Run("IncorrectHeaderParamArrayNumber", func(t *testing.T) {
1531+
err := IncorrectHeaderParamArrayNumber(param, "bad", sch, sch, "/test", "get", "{}")
1532+
require.NotNil(t, err)
1533+
require.Equal(t, 1, err.SpecLine)
1534+
require.Equal(t, 0, err.SpecCol)
1535+
})
1536+
1537+
t.Run("IncorrectPathParamArrayNumber", func(t *testing.T) {
1538+
err := IncorrectPathParamArrayNumber(param, "bad", sch, sch, "/test", "{}")
1539+
require.NotNil(t, err)
1540+
require.Equal(t, 1, err.SpecLine)
1541+
require.Equal(t, 0, err.SpecCol)
1542+
})
1543+
1544+
t.Run("IncorrectPathParamArrayInteger", func(t *testing.T) {
1545+
err := IncorrectPathParamArrayInteger(param, "bad", sch, sch, "/test", "{}")
1546+
require.NotNil(t, err)
1547+
require.Equal(t, 1, err.SpecLine)
1548+
require.Equal(t, 0, err.SpecCol)
1549+
})
1550+
1551+
t.Run("IncorrectPathParamArrayBoolean", func(t *testing.T) {
1552+
err := IncorrectPathParamArrayBoolean(param, "bad", sch, sch, "/test", "{}")
1553+
require.NotNil(t, err)
1554+
require.Equal(t, 1, err.SpecLine)
1555+
require.Equal(t, 0, err.SpecCol)
1556+
})
1557+
}

errors/request_errors.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
1+
// Copyright 2023-2026 Princess Beef Heavy Industries, LLC / Dave Shanley
22
// SPDX-License-Identifier: MIT
33

44
package errors
@@ -18,8 +18,17 @@ import (
1818
func RequestContentTypeNotFound(op *v3.Operation, request *http.Request, specPath string) *ValidationError {
1919
ct := request.Header.Get(helpers.ContentTypeHeader)
2020
var ctypes []string
21-
for pair := orderedmap.First(op.RequestBody.Content); pair != nil; pair = pair.Next() {
22-
ctypes = append(ctypes, pair.Key())
21+
var contentMap *orderedmap.Map[string, *v3.MediaType]
22+
specLine, specCol := 1, 0
23+
if op.RequestBody != nil {
24+
contentMap = op.RequestBody.Content
25+
for pair := orderedmap.First(op.RequestBody.Content); pair != nil; pair = pair.Next() {
26+
ctypes = append(ctypes, pair.Key())
27+
}
28+
if low := op.RequestBody.GoLow(); low != nil && low.Content.KeyNode != nil {
29+
specLine = low.Content.KeyNode.Line
30+
specCol = low.Content.KeyNode.Column
31+
}
2332
}
2433
return &ValidationError{
2534
ValidationType: helpers.RequestBodyValidation,
@@ -28,25 +37,30 @@ func RequestContentTypeNotFound(op *v3.Operation, request *http.Request, specPat
2837
request.Method, ct),
2938
Reason: fmt.Sprintf("The content type '%s' of the %s request submitted has not "+
3039
"been defined, it's an unknown type", ct, request.Method),
31-
SpecLine: op.RequestBody.GoLow().Content.KeyNode.Line,
32-
SpecCol: op.RequestBody.GoLow().Content.KeyNode.Column,
40+
SpecLine: specLine,
41+
SpecCol: specCol,
3342
Context: op,
34-
HowToFix: fmt.Sprintf(HowToFixInvalidContentType, orderedmap.Len(op.RequestBody.Content), strings.Join(ctypes, ", ")),
43+
HowToFix: fmt.Sprintf(HowToFixInvalidContentType, orderedmap.Len(contentMap), strings.Join(ctypes, ", ")),
3544
RequestPath: request.URL.Path,
3645
RequestMethod: request.Method,
3746
SpecPath: specPath,
3847
}
3948
}
4049

4150
func OperationNotFound(pathItem *v3.PathItem, request *http.Request, method string, specPath string) *ValidationError {
51+
specLine, specCol := 1, 0
52+
if low := pathItem.GoLow(); low != nil && low.KeyNode != nil {
53+
specLine = low.KeyNode.Line
54+
specCol = low.KeyNode.Column
55+
}
4256
return &ValidationError{
4357
ValidationType: helpers.RequestValidation,
4458
ValidationSubType: helpers.ValidationMissingOperation,
4559
Message: fmt.Sprintf("%s operation request content type '%s' does not exist",
4660
request.Method, method),
4761
Reason: fmt.Sprintf("The path was found, but there was no '%s' method found in the spec", request.Method),
48-
SpecLine: pathItem.GoLow().KeyNode.Line,
49-
SpecCol: pathItem.GoLow().KeyNode.Column,
62+
SpecLine: specLine,
63+
SpecCol: specCol,
5064
Context: pathItem,
5165
HowToFix: HowToFixPathMethod,
5266
RequestPath: request.URL.Path,

0 commit comments

Comments
 (0)