Skip to content

Commit

Permalink
Add policy compliance check binary. (#581)
Browse files Browse the repository at this point in the history
Adds a `check_policy_compliance` binary with the following usage:
```
check_policy_compliance: This tool takes an IR representation of a system and returns whether it is policy compliant.

  Flags from src/backends/policy_engine/souffle/check_policy_compliance.cc:
    --ir (the IR file); default: "";
    --sql_policy_rules (file containing the SQL policy rules); default: "";
```

This PR also adds a simple sh_test with the following usage:
```
Usage:
  check_policy_compliance_test.sh \
     pass|fail <ir_file> <sql_policy_rules_file>
```

The test script can be improved further. e.g., instead of adding 'pass` or `fail` expectations to the script, we can have it encoded in the given IR file itself (e.g., https://github.com/google-research/raksha/blob/953dee1139eb1d71e949d12010941cd3e63919a6/src/analysis/souffle/tests/arcs_manifest_tests_todo/fail_no_inputs.arcs#L3). However, we are starting simple to get this tool in place and unblock other work.

Closes #581

COPYBARA_INTEGRATE_REVIEW=#581 from google-research:policy_checker_bin@bgogul 7e5bb91
PiperOrigin-RevId: 457028665
  • Loading branch information
bgogul authored and arcs-c3po committed Jun 24, 2022
1 parent 54be913 commit 950990c
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 1 deletion.
47 changes: 47 additions & 0 deletions src/backends/policy_engine/souffle/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,53 @@ package(
licenses = ["notice"],
)

cc_binary(
name = "check_policy_compliance",
srcs = ["check_policy_compliance.cc"],
deps = [
":souffle_policy_checker",
"//src/backends/policy_engine:policy",
"//src/common/logging",
"//src/parser/ir:ir_parser",
"@absl//absl/flags:flag",
"@absl//absl/flags:parse",
"@absl//absl/flags:usage",
"@absl//absl/status:statusor",
],
)

sh_test(
name = "check_policy_compliance_pass_test",
srcs = ["check_policy_compliance_test.sh"],
args = [
"$(location :check_policy_compliance)",
"pass",
"$(location //src/backends/policy_engine/souffle/testdata:simple_passing_sql.ir)",
"$(location //src/backends/policy_engine/souffle/testdata:sql_policy_rules.txt)",
],
data = [
":check_policy_compliance",
"//src/backends/policy_engine/souffle/testdata:simple_passing_sql.ir",
"//src/backends/policy_engine/souffle/testdata:sql_policy_rules.txt",
],
)

sh_test(
name = "check_policy_compliance_fail_test",
srcs = ["check_policy_compliance_test.sh"],
args = [
"$(location :check_policy_compliance)",
"fail",
"$(location //src/backends/policy_engine/souffle/testdata:simple_failing_sql.ir)",
"$(location //src/backends/policy_engine/souffle/testdata:sql_policy_rules.txt)",
],
data = [
":check_policy_compliance",
"//src/backends/policy_engine/souffle/testdata:simple_failing_sql.ir",
"//src/backends/policy_engine/souffle/testdata:sql_policy_rules.txt",
],
)

cc_library(
name = "datalog_lowering_visitor",
srcs = ["datalog_lowering_visitor.cc"],
Expand Down
101 changes: 101 additions & 0 deletions src/backends/policy_engine/souffle/check_policy_compliance.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//-----------------------------------------------------------------------------
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//----------------------------------------------------------------------------

#include <filesystem>
#include <fstream>
#include <iostream>
#include <optional>
#include <sstream>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/flags/usage.h"
#include "absl/status/statusor.h"
#include "src/backends/policy_engine/policy.h"
#include "src/backends/policy_engine/souffle/souffle_policy_checker.h"
#include "src/common/logging/logging.h"
#include "src/parser/ir/ir_parser.h"

ABSL_FLAG(std::string, ir, "", "the IR file");
ABSL_FLAG(std::string, sql_policy_rules, "",
"file containing the SQL policy rules");

constexpr char kUsageMessage[] =
"This tool takes an IR representation of a system and returns whether it "
"is policy compliant.";

namespace {

absl::StatusOr<std::string> ReadFileContents(std::filesystem::path file_path) {
if (!std::filesystem::exists(file_path)) {
return absl::NotFoundError(
absl::StrCat("File '", file_path.string(), "' does not exist!"));
}

std::ifstream file_stream(file_path);
if (!file_stream) {
return absl::FailedPreconditionError(absl::StrCat(
"Unable to read file '", file_path.string(), "': ", strerror(errno)));
}

// Read the entire file as ostringstream and convert to std::string.
std::ostringstream string_stream;
string_stream << file_stream.rdbuf();
return string_stream.str();
}

} // namespace

using raksha::backends::policy_engine::Policy;
using raksha::backends::policy_engine::SoufflePolicyChecker;
using raksha::ir::IrProgramParser;
using raksha::ir::Module;

int main(int argc, char* argv[]) {
google::InitGoogleLogging("check_policy_compliance");
absl::SetProgramUsageMessage(kUsageMessage);
absl::ParseCommandLine(argc, argv);

// Parse the given IR file.
absl::StatusOr<std::string> ir_string =
ReadFileContents(absl::GetFlag(FLAGS_ir));
if (!ir_string.ok()) {
LOG(ERROR) << "Error reading IR file: " << ir_string.status();
return 2;
}

// Read the sql policy rules file.
absl::StatusOr<std::string> sql_policy_rules =
ReadFileContents(absl::GetFlag(FLAGS_sql_policy_rules));
if (!ir_string.ok()) {
LOG(ERROR) << "Error reading sql policy rules file: "
<< sql_policy_rules.status();
return 2;
}

// Invoke policy checker and return result.
SoufflePolicyChecker checker;
IrProgramParser ir_parser;
const Module& module = ir_parser.ParseProgram(*ir_string);
Policy policy(*sql_policy_rules);
if (checker.IsModulePolicyCompliant(module, policy)) {
LOG(ERROR) << "Policy check succeeded!";
return 0;
} else {
LOG(ERROR) << "Policy check failed!";
return 1;
}
}
67 changes: 67 additions & 0 deletions src/backends/policy_engine/souffle/check_policy_compliance_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/bash
#
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#-------------------------------------------------------------------------------

PASS_RESULT="pass"
FAIL_RESULT="fail"

function printUsageAndExit() {
echo "Usage: "
echo " check_policy_compliance_test.sh \ "
echo " <cmd> ${PASS_RESULT}|${FAIL_RESULT} <ir_file> <sql_policy_rules_file>"
exit 1
}

if [ $# -ne 4 ]; then
printUsageAndExit
fi

CMD_ARG=$1
EXPECTATION_ARG=$2
IR_FILE_ARG=$3
SQL_POLICY_RULES_FILE_ARG=$4

if
[ "${EXPECTATION_ARG}" != "${PASS_RESULT}" ] &&
[ "${EXPECTATION_ARG}" != "${FAIL_RESULT}" ];
then
printUsageAndExit
fi

# A simple script to test the `check_policy_compliance` command line tool.
ROOT_DIR=${TEST_SRCDIR}/${TEST_WORKSPACE}
CMD=${ROOT_DIR}/${CMD_ARG}
IR_FILE=${ROOT_DIR}/${IR_FILE_ARG}
SQL_POLICY_RULES_FILE=${ROOT_DIR}/${SQL_POLICY_RULES_FILE_ARG}

$CMD --ir=$IR_FILE --sql_policy_rules=$SQL_POLICY_RULES_FILE
CMD_RESULT=$?
if [ ${CMD_RESULT} -eq 0 ]; then
ACTUAL_RESULT="${PASS_RESULT}"
elif [ ${CMD_RESULT} -eq 1 ]; then
ACTUAL_RESULT="${FAIL_RESULT}"
else
ACTUAL_RESULT="UNKNOWN"
fi

if [ "${ACTUAL_RESULT}" != "${EXPECTATION_ARG}" ]; then
echo "Policy compliance check does not match expectations!"
echo " Expected: ${EXPECTATION_ARG}, Actual: ${ACTUAL_RESULT}"
exit 1;
else
exit 0
fi
27 changes: 27 additions & 0 deletions src/backends/policy_engine/souffle/testdata/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#-----------------------------------------------------------------------------
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#----------------------------------------------------------------------------

package(
default_visibility = ["//src:__subpackages__"],
features = ["layering_check"],
licenses = ["notice"],
)

exports_files([
"sql_policy_rules.txt",
"simple_passing_sql.ir",
"simple_failing_sql.ir",
])
10 changes: 10 additions & 0 deletions src/backends/policy_engine/souffle/testdata/simple_failing_sql.ir
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module m0 {
block b0 {
%0 = sql.merge [control_input_start_index: 0]()
%1 = sql.tag_transform [rule_name: set_restricted] (%0.out)
%2 = sql.tag_transform [rule_name: set_ssn_integrity] (%1.out)
// Policy check fails because we are not removing the restricted tag.
%3 = sql.merge [control_input_start_index: 1] (%2.out)
%4 = sql.sql_output [](%3.out)
} // block b0
} // module m0
10 changes: 10 additions & 0 deletions src/backends/policy_engine/souffle/testdata/simple_passing_sql.ir
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module m0 {
block b0 {
%0 = sql.merge [control_input_start_index: 0]()
%1 = sql.tag_transform [rule_name: set_restricted] (%0.out)
%2 = sql.tag_transform [rule_name: set_ssn_integrity] (%1.out)
// Policy check succeeds because we are removing the restricted tag.
%3 = sql.tag_transform [rule_name: remove_restricted_for_ssn] (%2.out)
%4 = sql.sql_output [](%3.out)
} // block b0
} // module m0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
["set_restricted", $AddConfidentialityTag("restricted"), nil]
["set_ssn_integrity", $AddIntegrityTag("SSN"), nil]
["remove_restricted_for_ssn", $RemoveConfidentialityTag("restricted"), nil]
2 changes: 1 addition & 1 deletion src/parser/ir/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ load("//build_defs:antlr.bzl", "antlr4_cc_combined")

package(
default_visibility = [
"//src/ir:__pkg__",
"//src:__subpackages__",
],
features = ["layering_check"],
licenses = ["notice"],
Expand Down

0 comments on commit 950990c

Please sign in to comment.