Skip to content

Commit 6b8be41

Browse files
committed
Tightens DMI output parser
Fixes an off-by-one error in the `tsh dmi-read` output parser and adds tests for same.
1 parent 005a2aa commit 6b8be41

2 files changed

Lines changed: 75 additions & 8 deletions

File tree

lib/devicetrust/native/device_linux.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import (
3030
"os"
3131
"os/exec"
3232
"os/user"
33-
"strings"
33+
"slices"
3434
"time"
3535

3636
"github.com/google/go-attestation/attest"
@@ -303,17 +303,23 @@ func readDMIInfoEscalated() (*linux.DMIInfo, error) {
303303
return nil, trace.Wrap(err, "running `sudo tsh device dmi-read`")
304304
}
305305

306-
// Strip any leading output before the first `{`, just in case.
307-
val := dmiOut.String()
308-
if n := strings.Index(val, "{"); n > 0 {
309-
val = val[n-1:]
306+
dmiInfo, err := parseDMIReadOutput(dmiOut.Bytes())
307+
if err != nil {
308+
return nil, trace.Wrap(err, "parsing dmi-read output")
310309
}
311310

312-
var dmiInfo linux.DMIInfo
313-
if err := json.Unmarshal([]byte(val), &dmiInfo); err != nil {
314-
return nil, trace.Wrap(err, "parsing dmi-read output")
311+
return dmiInfo, nil
312+
}
313+
314+
func parseDMIReadOutput(text []byte) (*linux.DMIInfo, error) {
315+
if n := slices.Index(text, '{'); n > 0 {
316+
text = text[n:]
315317
}
316318

319+
var dmiInfo linux.DMIInfo
320+
if err := json.Unmarshal(text, &dmiInfo); err != nil {
321+
return nil, trace.Wrap(err)
322+
}
317323
return &dmiInfo, nil
318324
}
319325

lib/devicetrust/native/device_linux_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,64 @@ func TestCollectDeviceData_linux(t *testing.T) {
204204
})
205205
}
206206
}
207+
208+
func TestParseDMIReadOutput(t *testing.T) {
209+
const validDMIJson = `{"ProductName":"21J50013US","ProductSerial":"PF0A0AAA","BoardSerial":"L1AA00A00A0","ChassisAssetTag":"SomeAssetTag"}`
210+
testCases := []struct {
211+
name string
212+
input []byte
213+
checkError require.ErrorAssertionFunc
214+
expected *linux.DMIInfo
215+
}{
216+
{
217+
name: "clean",
218+
input: []byte(validDMIJson),
219+
checkError: require.NoError,
220+
expected: &linux.DMIInfo{
221+
ProductName: "21J50013US",
222+
ProductSerial: "PF0A0AAA",
223+
BoardSerial: "L1AA00A00A0",
224+
ChassisAssetTag: "SomeAssetTag",
225+
},
226+
}, {
227+
name: "prefixed",
228+
input: []byte("somerandomgibberish" + validDMIJson),
229+
checkError: require.NoError,
230+
expected: &linux.DMIInfo{
231+
ProductName: "21J50013US",
232+
ProductSerial: "PF0A0AAA",
233+
BoardSerial: "L1AA00A00A0",
234+
ChassisAssetTag: "SomeAssetTag",
235+
},
236+
}, {
237+
name: "invalid utf8 prefix",
238+
input: []byte("\xe2\x80\x83" + validDMIJson),
239+
checkError: require.NoError,
240+
expected: &linux.DMIInfo{
241+
ProductName: "21J50013US",
242+
ProductSerial: "PF0A0AAA",
243+
BoardSerial: "L1AA00A00A0",
244+
ChassisAssetTag: "SomeAssetTag",
245+
},
246+
}, {
247+
name: "invalid json",
248+
input: []byte("{" + validDMIJson),
249+
checkError: require.Error,
250+
}, {
251+
name: "empty input",
252+
input: []byte{},
253+
checkError: require.Error,
254+
}, {
255+
name: "nil input",
256+
input: nil,
257+
checkError: require.Error,
258+
},
259+
}
260+
for _, testCase := range testCases {
261+
t.Run(testCase.name, func(t *testing.T) {
262+
out, err := parseDMIReadOutput(testCase.input)
263+
testCase.checkError(t, err)
264+
require.Equal(t, testCase.expected, out)
265+
})
266+
}
267+
}

0 commit comments

Comments
 (0)