Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish rendered policy artifacts #10585

Merged
9 changes: 9 additions & 0 deletions .github/workflows/gh-pages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
- name: Build Statistics
run: ninja html-stats html-profile-stats -j2
working-directory: ./build
- name: Render Policies (Using control files)
run: ninja render-policies -j2
working-directory: ./build
- name: Generate HTML pages
run: utils/generate_html_pages.sh $PAGES_DIR
shell: bash
Expand All @@ -48,3 +51,9 @@ jobs:
token: ${{ secrets.CONTENT_PAGES_TOKEN }}
git-config-name: openscap-ci
git-config-email: openscap-ci@gmail.com
- name: Upload artifact if the event is pull request
uses: actions/upload-artifact@v3
if: ${{ github.event_name == 'pull_request' }}
with:
name: built-content
path: ${{ env.PAGES_DIR }}
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ set(SSG_SHARED "${CMAKE_SOURCE_DIR}/shared")
set(SSG_SHARED_REFS "${SSG_SHARED}/references")
set(SSG_SHARED_TRANSFORMS "${SSG_SHARED}/transforms")
set(SSG_BUILD_SCRIPTS "${CMAKE_SOURCE_DIR}/build-scripts")
set(SSG_UTILS_SCRIPTS "${CMAKE_SOURCE_DIR}/utils")

message(STATUS "SCAP Security Guide ${SSG_VERSION}")
message(STATUS "(see ${CMAKE_SOURCE_DIR}/docs/manual/developer_guide.adoc for build instructions)")
Expand Down Expand Up @@ -315,6 +316,8 @@ add_custom_target(profile-stats)
add_custom_target(html-stats)
add_custom_target(html-profile-stats)

add_custom_target(render-policies)

if(PY_SPHINX AND PY_SPHINXCONTRIB.AUTOJINJA AND PY_SPHINX_RTD_THEME AND PY_MYST_PARSER)
message(STATUS "Enabling docs directory as system supports Sphinx builds.")
add_subdirectory("docs")
Expand Down
29 changes: 29 additions & 0 deletions cmake/SSGCommon.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,21 @@ macro(ssg_make_html_stats_for_product PRODUCT)
)
endmacro()

macro(ssg_render_policies_for_product PRODUCT CONTROL_FILES)
foreach(CONTROL_FILE IN LISTS CONTROL_FILES)
add_custom_command(
OUTPUT "${CMAKE_BINARY_DIR}/${PRODUCT}/rendered-policies/${CONTROL_FILE}.html"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${PYTHON_EXECUTABLE}" "${SSG_UTILS_SCRIPTS}/render-policy.py" --build-dir "${CMAKE_BINARY_DIR}" --output "${CMAKE_BINARY_DIR}/${PRODUCT}/rendered-policies/${CONTROL_FILE}.html" ${PRODUCT} "${CMAKE_SOURCE_DIR}/controls/${CONTROL_FILE}.yml"
DEPENDS generate-ssg-${PRODUCT}-ds.xml
COMMENT "[${PRODUCT}-render-policy-${CONTROL_FILE}] generating rendered policy for ${CONTROL_FILE}"
)

add_custom_target(${PRODUCT}-render-policy-${CONTROL_FILE}
DEPENDS "${CMAKE_BINARY_DIR}/${PRODUCT}/rendered-policies/${CONTROL_FILE}.html"
)
endforeach()
endmacro()

macro(ssg_make_all_tables PRODUCT)
add_custom_command(
OUTPUT "${CMAKE_BINARY_DIR}/tables/tables-${PRODUCT}-all.html"
Expand Down Expand Up @@ -775,6 +790,20 @@ macro(ssg_build_product PRODUCT)
add_dependencies(stats ${PRODUCT}-stats)
add_dependencies(profile-stats ${PRODUCT}-profile-stats)
ssg_make_html_stats_for_product(${PRODUCT})

file(GLOB CONTROL_FILEPATHS "${CMAKE_SOURCE_DIR}/controls/*.yml")

foreach(CONTROL_FILEPATH IN LISTS CONTROL_FILEPATHS)
get_filename_component(CONTROL_FILE ${CONTROL_FILEPATH} NAME_WE)
list(APPEND CONTROL_FILES ${CONTROL_FILE})
endforeach()

ssg_render_policies_for_product(${PRODUCT} ${CONTROL_FILES})

foreach(CONTROL_FILE IN LISTS CONTROL_FILES)
add_dependencies(render-policies ${PRODUCT}-render-policy-${CONTROL_FILE})
endforeach()

add_dependencies(html-stats ${PRODUCT}-html-stats)
add_dependencies(html-profile-stats ${PRODUCT}-html-profile-stats)

Expand Down
6 changes: 1 addition & 5 deletions utils/gen_html_guides_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,9 @@ def create_index(data, template_name, output_filename):
def get_data(ssg_root):
products = []
p = pathlib.Path(ssg_root)
for product_file in p.glob("**/product.yml"):
for product_file in p.glob("products/**/product.yml"):
product_dir = product_file.parent
product_id = product_dir.name
# disregard folders that contain samples of product.yml files
# they are not a real product folder
if product_id in ["data", "example", "test_playbook_builder_data"]:
continue
with open(product_file, "r") as f:
product_yaml = yaml.full_load(f)
product_name = product_yaml["full_name"]
Expand Down
75 changes: 75 additions & 0 deletions utils/gen_rendered_policies_index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/python3

import argparse
import os
import pathlib
import sys
import yaml
from collections import namedtuple

import ssg.jinja
from utils.template_renderer import FlexibleLoader

# Helper script used to generate an HTML page to display rendered policies.

Product = namedtuple("Product", ["id", "name"])
Policy = namedtuple("Policy", ["id", "name"])


def create_index(data, template_name, output_filename):
html_jinja_template = os.path.join(
os.path.dirname(__file__), template_name)
env = ssg.jinja._get_jinja_environment(dict())
env.loader = FlexibleLoader(os.path.dirname(html_jinja_template))
result = ssg.jinja.process_file(html_jinja_template, data)
with open(output_filename, "wb") as f:
f.write(result.encode('utf8', 'replace'))


def get_control_files(ssg_root):
control_files = []
p = pathlib.Path(ssg_root)
for control_file in p.glob("controls/*.yml"):
# only process files, ignore controls directories
if not os.path.isfile(control_file):
continue
policy_id = pathlib.Path(control_file).stem
with open(control_file, "r") as f:
policy_yaml = yaml.full_load(f)
policy_name = policy_yaml["policy"]
policy = Policy(id=policy_id, name=policy_name)
control_files.append(policy)
return control_files


def get_data(ssg_root):
products = []
p = pathlib.Path(ssg_root)
for product_file in p.glob("products/**/product.yml"):
product_dir = product_file.parent
product_id = product_dir.name

# skip if there are not built rendered-polices
if not os.path.isdir(os.path.join(ssg_root, "build", product_id, "rendered-policies")):
continue
with open(product_file, "r") as f:
product_yaml = yaml.full_load(f)
product_name = product_yaml["full_name"]
product = Product(id=product_id, name=product_name)
products.append(product)
data = {"products": products}
return data


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"ssg_root",
help="Path to the root directory of scap-security-guide")
parser.add_argument(
"output",
help="Path where the output HTML file should be generated")
args = parser.parse_args()
data = get_data(args.ssg_root)
data.update({"control_files": get_control_files(args.ssg_root)})
create_index(data, "html_rendered_policies_index_template.html", args.output)
22 changes: 22 additions & 0 deletions utils/generate_html_pages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,27 @@ echo "</body>" >> index.html
echo "</html>" >> index.html
popd


# Generate Rendered Policies page
POLICY_DIR="$PAGES_DIR/rendered-policies"
ggbecker marked this conversation as resolved.
Show resolved Hide resolved
mkdir -p "$POLICY_DIR"
products=$(echo -e "import ssg.constants\nprint(ssg.constants.product_directories)" | python3 | sed -s "s/'//g; s/,//g; s/\[//g; s/\]//g")
for product in $products
do
if [ -d build/$product ]; then
mkdir -p "$POLICY_DIR/$product"
if [ -d "build/$product/rendered-policies/" ]; then
cp -rf "build/${product}/rendered-policies/"* "$POLICY_DIR/$product/"
fi
fi
done
utils/gen_rendered_policies_index.py . "$PAGES_DIR/rendered-policies/index.html"
retVal=$?
if [ $retVal -ne 0 ]; then
echo "Something wrong happened while generating the HTML Rendered Policy Index page"
exit 1
fi

pushd $PAGES_DIR
touch index.html
echo "<!DOCTYPE html>" > index.html
Expand All @@ -96,6 +117,7 @@ echo "<li><a href=\"statistics/index.html\">Statistics</a></li>" >> index.html
echo "<li><a href=\"guides/index.html\">Guides</a></li>" >> index.html
echo "<li><a href=\"tables/index.html\">Mapping Tables</a></li>" >> index.html
echo "<li><a href=\"srg_mapping/index.html\">SRG Mapping Tables</a></li>" >> index.html
echo "<li><a href=\"rendered-policies/index.html\">Rendered Policies</a></li>" >> index.html
echo "</ul>" >> index.html
echo "</body>" >> index.html
echo "</html>" >> index.html
Expand Down
25 changes: 25 additions & 0 deletions utils/html_rendered_policies_index_template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Rendered Policies</title>
<style>
</style>
</head>
<body>

<h1>Rendered Policies</h1>

{{% for product in products|sort(attribute="name") %}}
<h4>{{{ product.name }}}</h4>
<ul>
{{% for control_file in control_files|sort(attribute="id") %}}
<li>
<a href="{{{ product.id }}}/{{{ control_file.id }}}.html">{{{ control_file.id }}}: {{{ control_file.name }}}</a>
</li>
{{% endfor %}}
</ul>
{{% endfor %}}

</body>
</html>
4 changes: 2 additions & 2 deletions utils/rendering/controls-template.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ <h2>{{{ control.id }}}: {{{ control.title }}}</h2>
{{%- endfor %}}
</ul>
<p>Automated: {{{ control.automated }}}</p>
{{% if control.rules -%}}
{{% if control.selections -%}}
Selections:
<ul>
{{%- for ruleobj in control.rules -%}}
{{%- for ruleobj in control.selections -%}}
{{%- set rule_id = ruleobj|string %}}
{{%- if rule_id in rules %}}
<li><a href="https://github.com/ComplianceAsCode/content/tree/master/{{{ rules[rule_id].relative_definition_location }}}">{{{ rule_id }}}</a>: {{{ rules[rule_id].title }}}</li>
Expand Down
3 changes: 3 additions & 0 deletions utils/template_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ def output_results(self, args):
if not args.output:
print(result)
else:
dir = os.path.dirname(args.output)
if not os.path.exists(dir):
os.makedirs(dir)
with open(args.output, "wb") as outfile:
result_for_output = result.encode('utf8', 'replace')
outfile.write(result_for_output)
Expand Down