• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 2217914841

16 Dec 2025 02:09PM UTC coverage: 79.682% (-0.05%) from 79.734%
2217914841

push

gitlab-ci

web-flow
Merge pull request #1863 from michalkopczan/MEN-9098-out-of-bounds-check

fix: Sanitize header list of payloads and corresponding type-info files

8 of 12 new or added lines in 3 files covered. (66.67%)

33 existing lines in 1 file now uncovered.

7871 of 9878 relevant lines covered (79.68%)

13898.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.16
/src/artifact/parser.cpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <artifact/parser.hpp>
16

17
#include <cstdint>
18
#include <memory>
19
#include <system_error>
20
#include <unordered_map>
21

22
#include <common/json.hpp>
23
#include <common/log.hpp>
24
#include <artifact/tar/tar.hpp>
25
#include <common/common.hpp>
26

27
#include <artifact/lexer.hpp>
28
#include <artifact/tar/tar.hpp>
29
#include <artifact/sha/sha.hpp>
30

31
namespace mender {
32
namespace artifact {
33
namespace parser {
34

35
using namespace std;
36

37
namespace lexer = artifact::lexer;
38
namespace log = mender::common::log;
39
namespace io = mender::common::io;
40
namespace tar = mender::tar;
41
namespace expected = mender::common::expected;
42
namespace error = mender::common::error;
43

44
namespace version = mender::artifact::v3::version;
45
namespace manifest = mender::artifact::v3::manifest;
46
namespace manifest_sig = mender::artifact::v3::manifest_sig;
47
namespace payload = mender::artifact::v3::payload;
48

49
ExpectedArtifact VerifyEmptyPayloadArtifact(
6✔
50
        Artifact &artifact, lexer::Lexer<token::Token, token::Type> &lexer) {
51
        if (artifact.header.subHeaders.size() != 1) {
6✔
NEW
52
                return expected::unexpected(parser_error::MakeError(
×
53
                        parser_error::Code::ParseError,
NEW
54
                        "No type-info found in the header. One must be present."));
×
55
        }
56

57
        // No meta-data allowed
58
        if (artifact.header.subHeaders.at(0).metadata) {
6✔
59
                return expected::unexpected(parser_error::MakeError(
×
60
                        parser_error::Code::ParseError,
61
                        "Empty payload Artifacts cannot contain a meta-data section"));
×
62
        }
63
        // TODO - When augmented sections are added - check for these also
64
        log::Trace("Empty payload Artifact: Verifying empty payload");
12✔
65
        auto expected_payload = artifact.Next();
6✔
66
        if (expected_payload) {
6✔
67
                // If a payload is present, verify that it is empty
68
                auto payload = expected_payload.value();
12✔
69
                auto expected_payload_file = payload.Next();
6✔
70
                if (expected_payload_file) {
6✔
71
                        return expected::unexpected(parser_error::MakeError(
×
72
                                parser_error::Code::ParseError, "Empty Payload Artifacts cannot have a payload"));
×
73
                } else if (
6✔
74
                        expected_payload_file.error().code
75
                        != parser_error::MakeError(parser_error::Code::NoMorePayloadFilesError, "").code) {
6✔
76
                        return expected::unexpected(
×
77
                                expected_payload.error().WithContext("While verifying empty payload"));
×
78
                } // else fall through
79
        } else if (
×
80
                expected_payload.error().code != parser_error::MakeError(parser_error::EOFError, "").code) {
×
81
                return expected::unexpected(
×
82
                        expected_payload.error().WithContext("While verifying empty payload"));
×
83
        } // else fall through
84
        return artifact;
6✔
85
}
86

87
ExpectedArtifact Parse(io::Reader &reader, config::ParserConfig config) {
164✔
88
        std::shared_ptr<tar::Reader> tar_reader {make_shared<tar::Reader>(reader)};
164✔
89

90
        auto lexer = lexer::Lexer<token::Token, token::Type> {tar_reader};
328✔
91

92
        token::Token tok = lexer.Next();
164✔
93

94
        log::Trace("Parsing Version");
328✔
95
        if (tok.type != token::Type::Version) {
164✔
96
                return expected::unexpected(parser_error::MakeError(
1✔
97
                        parser_error::Code::ParseError,
98
                        "Got unexpected token : '" + tok.TypeToString() + "' expected 'version'"));
3✔
99
        }
100

101
        auto expected_version = version::Parse(*tok.value);
163✔
102

103
        if (!expected_version) {
163✔
104
                return expected::unexpected(parser_error::MakeError(
×
105
                        parser_error::Code::ParseError,
106
                        "Failed to parse the version: " + expected_version.error().message));
×
107
        }
108

109
        auto version = expected_version.value();
326✔
110

111
        log::Trace("Parsing the Manifest");
326✔
112
        tok = lexer.Next();
163✔
113
        if (tok.type != token::Type::Manifest) {
163✔
114
                return expected::unexpected(parser_error::MakeError(
×
115
                        parser_error::Code::ParseError,
116
                        "Got unexpected token " + tok.TypeToString() + " expected 'manifest'"));
×
117
        }
118
        auto expected_manifest = manifest::Parse(*tok.value);
163✔
119
        if (!expected_manifest) {
163✔
120
                return expected::unexpected(parser_error::MakeError(
×
121
                        parser_error::Code::ParseError,
122
                        "Failed to parse the manifest: " + expected_manifest.error().String()));
×
123
        }
124
        auto manifest = expected_manifest.value();
326✔
125

126
        tok = lexer.Next();
163✔
127
        optional<ManifestSignature> signature;
163✔
128

129
        // When configured for signed artifacts, refuse installing non signed ones
130
        if (config.verify_signature != config::Signature::Skip
163✔
131
                and config.artifact_verify_keys.size() > 0 and tok.type != token::Type::ManifestSignature) {
163✔
132
                return expected::unexpected(parser_error::MakeError(
×
133
                        parser_error::Code::SignatureVerificationError,
134
                        "expecting signed artifact, but no signature file found"));
×
135
        }
136

137
        if (tok.type == token::Type::ManifestSignature) {
163✔
138
                auto expected_signature = manifest_sig::Parse(*tok.value);
6✔
139
                if (!expected_signature) {
6✔
140
                        return expected::unexpected(parser_error::MakeError(
×
141
                                parser_error::Code::ParseError,
142
                                "Failed to parse the manifest signature: " + expected_signature.error().message));
×
143
                }
144
                signature = expected_signature.value();
6✔
145
                tok = lexer.Next();
6✔
146

147
                // Verify the signature
148
                if (config.verify_signature != config::Signature::Skip
6✔
149
                        and config.artifact_verify_keys.size() > 0) {
6✔
150
                        auto expected_verified = manifest_sig::VerifySignature(
151
                                *signature, manifest.shasum, config.artifact_verify_keys);
4✔
152
                        if (!expected_verified) {
4✔
153
                                return expected::unexpected(parser_error::MakeError(
1✔
154
                                        parser_error::Code::SignatureVerificationError,
155
                                        "Failed to verify the manifest signature: "
156
                                                + expected_verified.error().message));
3✔
157
                        }
158
                        if (!expected_verified.value()) {
3✔
159
                                return expected::unexpected(parser_error::MakeError(
×
160
                                        parser_error::Code::SignatureVerificationError,
161
                                        "Wrong manifest signature or wrong key"));
×
162
                        }
163
                }
164
        }
165

166
        log::Trace("Check version integrity");
324✔
167
        if (manifest.Get("version") != version.shasum.String()) {
162✔
168
                return expected::unexpected(sha::MakeError(
1✔
169
                        sha::ShasumMismatchError,
170
                        "The checksum of version file does not match the expected checksum, (expected): "
171
                                + manifest.Get("version") + " (calculated): " + version.shasum.String()));
3✔
172
        }
173

174
        log::Trace("Parsing the Header");
322✔
175
        if (tok.type != token::Type::Header) {
161✔
176
                return expected::unexpected(parser_error::MakeError(
×
177
                        parser_error::Code::ParseError,
178
                        "Got unexpected token " + tok.TypeToString() + " expected 'Header'"));
×
179
        }
180
        sha::Reader shasum_reader {*tok.value, manifest.Get("header.tar")};
483✔
181
        auto expected_header = v3::header::Parse(shasum_reader, config);
322✔
182
        if (!expected_header) {
161✔
183
                return expected::unexpected(parser_error::MakeError(
6✔
184
                        parser_error::Code::ParseError,
185
                        "Failed to parse the header: " + expected_header.error().message));
18✔
186
        }
187
        auto header = expected_header.value();
310✔
188

189
        // Create the object
190
        auto artifact = Artifact {version, manifest, header, lexer};
465✔
191
        if (signature) {
155✔
192
                artifact.manifest_signature = signature;
193
        }
194

195
        // Check the empty payload structure
196
        if (header.info.payloads.at(0).type == v3::header::Payload::EmptyPayload) {
155✔
197
                auto expected_empty_payload_artifact = VerifyEmptyPayloadArtifact(artifact, lexer);
6✔
198
                if (!expected_empty_payload_artifact) {
6✔
199
                        return expected_empty_payload_artifact;
200
                }
201
                return artifact;
6✔
202
        }
203

204
        return artifact;
149✔
205
};
206

207

208
ExpectedPayload Artifact::Next() {
241✔
209
        token::Token tok = lexer_.Next();
241✔
210
        if (payload_index_ != 0) {
241✔
211
                // Currently only one payload supported
212
                switch (tok.type) {
101✔
213
                case token::Type::EOFToken:
214
                        return expected::unexpected(parser_error::MakeError(
101✔
215
                                parser_error::Code::EOFError, "Reached the end of the Artifact"));
303✔
216
                case token::Type::Payload:
217
                        return expected::unexpected(error::Error(
×
218
                                make_error_condition(errc::not_supported), "Only one artifact payload supported"));
×
219
                default:
×
220
                        return expected::unexpected(parser_error::MakeError(
×
221
                                parser_error::Code::ParseError, "Unexpected token: " + tok.TypeToString()));
×
222
                }
223
        }
224

225
        if (tok.type != token::Type::Payload) {
140✔
226
                return expected::unexpected(parser_error::MakeError(
×
227
                        parser_error::Code::ParseError,
228
                        "Got unexpected token " + tok.TypeToString() + " expected 'data/0000.tar"));
×
229
        }
230

231
        log::Trace("Parsing the payload");
280✔
232
        payload_index_++;
140✔
233
        return payload::Payload(*(this->lexer_.current.value), manifest);
140✔
234
}
235

236
} // namespace parser
237
} // namespace artifact
238
} // namespace mender
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc