Skip to content

Conversation

jouho
Copy link
Contributor

@jouho jouho commented Sep 10, 2025

Release Summary:

Resolved issues:

Description of changes:

Adds a utility function to the policy builder for outputting a breakdown of a security policy. We define a V1 format that we currently use for the policy snapshot utility, and replace it to use this policy builder utility instead. I essentially moved the bin/policy.c utility into the policy builder. In the long term, we plan to add more formalized formats which we can add as V2, V3, etc .

Call-outs:

I ran the following command to regenerate all the snapshots and verify that the new policy output utility is working:

./tests/policy_snapshot/generate.sh build/bin/policy ./tests/policy_snapshot/snapshots

As a result, some snapshots were updated.

Testing:

Added unit tests for happy paths as well as negative paths. We have snapshot test that checks for exact content match.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@github-actions github-actions bot added the s2n-core team label Sep 10, 2025
@@ -1,4 +1,4 @@
name: ELBSecurityPolicy-2016-08
name: ELBSecurityPolicy-TLS-1-0-2015-05
Copy link
Contributor Author

@jouho jouho Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this seems confusing, it's expected behavior because the tool searches the security_policy_selection array to find the first matching policy object. Although the names are different, they both point to the same exact policy configuration.

This might be confusing, but I don't think it will be an issue for someone trying to understand the components of the security policy. I considered two alternatives:

  • List all aliases: Show all policy names that map to the same policy object. This would be useful for identifying equivalent policies but comes with a performance cost.

  • Retain the user's input: Store whatever the user requested initially when building the policy builder and use that value for the name. This would require adding a new field to the s2n_security_policy_builder struct, but since we don't have other use cases for that field, I'm not sure if we want to commit to it yet.

I'd appreciate any opinions on this.

@jouho jouho marked this pull request as ready for review September 11, 2025 22:55
Comment on lines +162 to +163
* FORMAT_V1: Human-readable YAML-style format
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really YAML? If it is, it's a coincidence 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're claiming this is YAML should we try to parse the results with a YAML parser maybe?

Comment on lines +195 to +197
* @param fd The file descriptor to write to (e.g., STDOUT_FILENO or an open file)
* @returns S2N_SUCCESS on success, S2N_FAILURE on failure
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think writing to a file descriptor is sufficient? Would customers want to write to memory instead? This might be particularly important bc I don't believe any of our current APIs write to a file descriptor yet.

Comment on lines +187 to +188
/* Future formats can be added here */
} s2n_policy_format;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Idk about this placeholder comment in what will eventually be a public enum

Comment on lines +167 to +168
* - rules:
* - <rule_name>: <yes|no>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would true/false be more normal, if we're calling this YAML?

Comment on lines +198 to +199
int s2n_policy_builder_write_verbose(struct s2n_security_policy_builder *builder,
s2n_policy_format format, int fd);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be operating on the s2n_security_policy itself, not the builder. We want to print the actual policies, and we want it to be applicable to policies regardless of origin.

Comment on lines +329 to +336
/* TODO: Currently outputs the base_policy. This needs to be updated once builder implements a "finalized" policy field */
const char *policy_name = "unknown";
for (size_t i = 0; security_policy_selection[i].version != NULL; i++) {
if (security_policy_selection[i].security_policy == builder->base_policy) {
policy_name = security_policy_selection[i].version;
break;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal here isn't to print existing, hard-coded policies (except to share code with bin/policy.c). The goal is to print the newly constructed policies, to see the results of the builder. That means you cannot rely on security_policy_selection. The new policy won't be in security_policy_selection.

* - kem groups:
* -- <kem_group_name>
*/
S2N_POLICY_FORMAT_V1 = 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of version numbers for the policy formats? Is the thought that we can't update our format at all because we might break users? Does that mean we'll potentially have two very similar YAML outputs if we want to make a minor change to V1?

In this case I wonder if it'd be worth giving the formats a name in addition to the version? Like

S2N_POLICY_FORMAT_YAML_V1 = 1,
S2N_POLICY_FORMAT_YAML_V2 = 2,
S2N_POLICY_FORMAT_XML_V1 = 3,

Otherwise it might be strange to have V1 be YAML, V2 be some completely other thing, and V3 be a slight modification to the V1 YAML?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if policy_builder is the right place for this stuff. It seems like it might pollute the actual security policy builder logic if we add a bunch of new output formats? Maybe s2n_policy_writer.c or something? Not sure.


POSIX_GUARD(s2n_write_fd_formatted(fd, "name: %s\n", policy_name));

const char *version_str = version_strs[policy->minimum_protocol_version];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to check that policy->minimum_protocol_version isn't too large for version_strs. Though unlikely, I could see special values being used to mean "any version" or something, and we wouldn't want to over-read version_strs.

}

/* Write verbose policy output in FORMAT_V1 style */
static int s2n_policy_write_format_v1(const struct s2n_security_policy *policy, const char *policy_name, int fd)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally internal functions would return S2N_RESULT. See

* The goal of s2n_result is to provide a strongly-typed error

Comment on lines +162 to +163
* FORMAT_V1: Human-readable YAML-style format
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're claiming this is YAML should we try to parse the results with a YAML parser maybe?

RESULT_ENSURE(buffer_size > 0, S2N_ERR_INVALID_ARGUMENT);

/* Use a pipe for capturing output */
int pipe_fds[2];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiler might not initialize your variables for you, so it's best to do it manually. Having your variables initialized with garbage can lead to undefined behavior.

Suggested change
int pipe_fds[2];
int pipe_fds[2] = { 0 };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants