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

mendersoftware / mender / 2314337760

03 Feb 2026 07:39PM UTC coverage: 75.808% (-0.1%) from 75.944%
2314337760

push

gitlab-ci

web-flow
Merge pull request #1893 from mendersoftware/cherry-5.0.x-master

[Cherry 5.0.x]: fix: add Host header to proxy HTTP CONNECT request

2 of 2 new or added lines in 1 file covered. (100.0%)

402 existing lines in 16 files now uncovered.

7508 of 9904 relevant lines covered (75.81%)

13703.53 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✔
UNCOV
52
                return expected::unexpected(parser_error::MakeError(
×
53
                        parser_error::Code::ParseError,
UNCOV
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✔
UNCOV
59
                return expected::unexpected(parser_error::MakeError(
×
60
                        parser_error::Code::ParseError,
UNCOV
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(
×
UNCOV
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(
×
UNCOV
77
                                expected_payload.error().WithContext("While verifying empty payload"));
×
78
                } // else fall through
UNCOV
79
        } else if (
×
UNCOV
80
                expected_payload.error().code != parser_error::MakeError(parser_error::EOFError, "").code) {
×
UNCOV
81
                return expected::unexpected(
×
UNCOV
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) {
182✔
88
        std::shared_ptr<tar::Reader> tar_reader {make_shared<tar::Reader>(reader)};
182✔
89

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

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

94
        log::Trace("Parsing Version");
364✔
95
        if (tok.type != token::Type::Version) {
182✔
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
        // Note that we're not verifying the version file's checksum here because we do not know
102
        // the expected checksum at this point yet.
103
        auto expected_version = version::Parse(*tok.value);
181✔
104

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

111
        auto version = expected_version.value();
362✔
112

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

128
        tok = lexer.Next();
181✔
129
        optional<ManifestSignature> signature;
181✔
130

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

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

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

168
        // Manually check the version file's checksum as it is available to us now.
169
        log::Trace("Check version integrity");
360✔
170
        if (manifest.Get("version") != version.shasum.String()) {
180✔
171
                return expected::unexpected(sha::MakeError(
1✔
172
                        sha::ShasumMismatchError,
173
                        "The checksum of version file does not match the expected checksum, (expected): "
174
                                + manifest.Get("version") + " (calculated): " + version.shasum.String()));
3✔
175
        }
176

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

192
        // Create the object
193
        auto artifact = Artifact {version, manifest, header, lexer};
519✔
194
        if (signature) {
173✔
195
                artifact.manifest_signature = signature;
196
        }
197

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

207
        return artifact;
167✔
208
};
209

210

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

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

234
        log::Trace("Parsing the payload");
316✔
235
        payload_index_++;
158✔
236
        return payload::Payload(*(this->lexer_.current.value), manifest);
158✔
237
}
238

239
} // namespace parser
240
} // namespace artifact
241
} // 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

© 2026 Coveralls, Inc