-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add GitHub Action and Docker support #72
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
Merged
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
7240037
feat: expand detector coverage and fix regex patterns
pixincreate e5cf31d
fix: add CRITICAL severity support (was silently downgraded to LOW)
pixincreate 16d1072
feat: add baseline suppression for known findings
pixincreate 4a2bbdc
fix: resolve merge conflicts and restore full feature implementation
pixincreate 2c421c8
chore: put omo in gitignore
pixincreate beee475
fix: scan_stream chunk overlap, salted SHA-256 baseline, real stdin i…
pixincreate 82abd35
feat: add GitHub Action composite action and Dockerfile
pixincreate 2e62f03
fix: address PR review issues - remove eval, add --locked, non-root u…
pixincreate 6e104bd
docs(changelog): add PR review fix entries
pixincreate 6c1e335
docs(changelog): add GitHub Action and Docker entries
pixincreate cf8fded
Merge remote-tracking branch 'origin/master' into feat/missing-features
pixincreate ba70d14
Merge remote-tracking branch 'origin/feat/missing-features' into feat…
pixincreate b6dfa24
chore: bump GitHub Actions to latest versions, minimize Docker image …
pixincreate 8019783
refactor: use const for baseline version and domain separator, rename…
pixincreate 916d591
Merge remote-tracking branch 'origin/feat/missing-features' into feat…
pixincreate 3f0f0b2
chore: upgrade workflow actions to latest, add git to Docker image
pixincreate 9329a92
fix: stabilize baseline fingerprints
pixincreate 5f5fc98
fix: validate detector severity and names
pixincreate df0669a
fix: harden file and git history scanning
pixincreate 1113feb
fix: harden generated git hooks
pixincreate 3d6d955
style: group Rust imports
pixincreate d6b9ffb
fix: harden composite scan action
pixincreate 99e9f2c
fix: align Docker and release publishing
pixincreate 4ce00f9
Merge remote-tracking branch 'origin/feat/missing-features' into feat…
pixincreate eedadcb
fix: guard Unix-only hook test imports
pixincreate 699f621
Merge branch 'feat/missing-features' into feat/github-action-docker
pixincreate ec06083
refactor: clarify pre-push remote policy shell
pixincreate 15dc56a
refactor: simplify baseline hashing
pixincreate 60d25b0
style: use descriptive scanner test errors
pixincreate c40a836
Merge branch 'feat/missing-features' into feat/github-action-docker
pixincreate 26287b8
Merge master into feat/github-action-docker
pixincreate File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| .git/ | ||
| .gitignore | ||
| .github/ | ||
| target/ | ||
| *.md | ||
| COMPARISON.md | ||
| **/*.rs.bk | ||
| .DS_Store |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,281 @@ | ||
| name: 'KeyWatch Scan' | ||
| description: 'Scan files and directories for secrets with KeyWatch' | ||
| author: 'Pa1Nark' | ||
|
|
||
| inputs: | ||
| paths: | ||
| description: 'Paths to scan (space-separated, supports globs)' | ||
| required: false | ||
| default: '.' | ||
| args: | ||
| description: 'Additional CLI arguments to pass to key-watch' | ||
| required: false | ||
| default: '' | ||
| exit-mode: | ||
| description: 'Exit code behavior: strict, critical, or always' | ||
| required: false | ||
| default: 'strict' | ||
| output: | ||
| description: 'Path to write the JSON report file' | ||
| required: false | ||
| default: '' | ||
| verbose: | ||
| description: 'Deprecated: verbose scanner output is disabled to avoid logging matched secrets' | ||
| required: false | ||
| default: 'false' | ||
|
|
||
| outputs: | ||
| findings-count: | ||
| description: 'Number of findings detected' | ||
| value: ${{ steps.scan.outputs.findings_count }} | ||
| exit-code: | ||
| description: 'Exit code from the scan' | ||
| value: ${{ steps.scan.outputs.exit_code }} | ||
|
|
||
| runs: | ||
| using: 'composite' | ||
| steps: | ||
| - name: Install KeyWatch | ||
| shell: bash | ||
| env: | ||
| GITHUB_TOKEN: ${{ github.token }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| for required_tool in curl jq; do | ||
| if ! command -v "$required_tool" >/dev/null 2>&1; then | ||
| echo "ERROR: $required_tool is required on this runner" >&2 | ||
| exit 1 | ||
| fi | ||
| done | ||
|
|
||
| REPO="pixincreate/KeyWatch" | ||
| VERSION="${KEYWATCH_VERSION:-latest}" | ||
|
|
||
| curl_args=(-fsSL) | ||
| if [ -n "${GITHUB_TOKEN:-}" ]; then | ||
| curl_args+=(-H "Authorization: Bearer ${GITHUB_TOKEN}") | ||
| fi | ||
|
|
||
| if [ "$VERSION" = "latest" ]; then | ||
| release_json=$(curl "${curl_args[@]}" \ | ||
| "https://api.github.com/repos/$REPO/releases/latest") | ||
| VERSION=$(jq -er '.tag_name' <<<"$release_json") | ||
| elif [[ "$VERSION" != v* ]]; then | ||
| VERSION="v$VERSION" | ||
| fi | ||
|
|
||
| if [[ ! "$VERSION" =~ ^v[0-9A-Za-z._-]+$ ]]; then | ||
| echo "ERROR: invalid KeyWatch version/tag '$VERSION'" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| case "$RUNNER_OS" in | ||
| Linux) asset_os="linux"; exe_suffix="" ;; | ||
| macOS) asset_os="darwin"; exe_suffix="" ;; | ||
| Windows) | ||
| echo "ERROR: Windows runners are not supported by this composite action" >&2 | ||
| exit 1 | ||
| ;; | ||
| *) | ||
| echo "ERROR: unsupported runner OS: $RUNNER_OS" >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
|
|
||
| case "$RUNNER_ARCH" in | ||
| X64) asset_arch="x86_64" ;; | ||
| ARM64) | ||
| if [ "$asset_os" = "darwin" ]; then | ||
| asset_arch="aarch64" | ||
| else | ||
| echo "ERROR: unsupported runner architecture for $RUNNER_OS: $RUNNER_ARCH" >&2 | ||
| exit 1 | ||
| fi | ||
| ;; | ||
| *) | ||
| echo "ERROR: unsupported runner architecture: $RUNNER_ARCH" >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
|
|
||
| install_dir="$RUNNER_TEMP/keywatch/$VERSION" | ||
| bin_dir="$install_dir/bin" | ||
| config_path="$install_dir/detectors.toml" | ||
| binary_path="$bin_dir/key-watch$exe_suffix" | ||
| asset_name="keywatch-$asset_os-$asset_arch$exe_suffix" | ||
| binary_url="https://github.com/$REPO/releases/download/$VERSION/$asset_name" | ||
| config_url="https://raw.githubusercontent.com/$REPO/$VERSION/detectors.toml" | ||
|
|
||
| mkdir -p "$bin_dir" | ||
|
|
||
| echo "Downloading KeyWatch $VERSION for $asset_os-$asset_arch..." | ||
| curl -fsSL "$binary_url" -o "$binary_path" | ||
| chmod +x "$binary_path" | ||
|
|
||
| echo "Downloading KeyWatch detectors config from $VERSION..." | ||
| curl -fsSL "$config_url" -o "$config_path" | ||
|
|
||
| export PATH="$bin_dir:$PATH" | ||
| export KEYWATCH_CONFIG_PATH="$config_path" | ||
| echo "$bin_dir" >> "$GITHUB_PATH" | ||
| echo "KEYWATCH_CONFIG_PATH=$config_path" >> "$GITHUB_ENV" | ||
|
|
||
| echo "Installed KeyWatch $VERSION ($asset_name)" | ||
| key-watch --version | ||
|
|
||
| - name: Run KeyWatch Scan | ||
| id: scan | ||
| shell: bash | ||
| env: | ||
| INPUT_PATHS: ${{ inputs.paths }} | ||
| INPUT_ARGS: ${{ inputs.args }} | ||
| INPUT_EXIT_MODE: ${{ inputs.exit-mode }} | ||
| INPUT_OUTPUT: ${{ inputs.output }} | ||
| INPUT_VERBOSE: ${{ inputs.verbose }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| for required_tool in jq; do | ||
| if ! command -v "$required_tool" >/dev/null 2>&1; then | ||
| echo "ERROR: $required_tool is required on this runner" >&2 | ||
| exit 1 | ||
| fi | ||
| done | ||
|
|
||
| if [ -z "${KEYWATCH_CONFIG_PATH:-}" ] || [ ! -f "$KEYWATCH_CONFIG_PATH" ]; then | ||
| echo "ERROR: KEYWATCH_CONFIG_PATH is not set to a readable detectors.toml" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| case "$INPUT_EXIT_MODE" in | ||
| always|critical|strict) ;; | ||
| *) | ||
| echo "ERROR: invalid exit-mode '$INPUT_EXIT_MODE' (expected: always, critical, strict)" >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
|
|
||
| verbose_normalized=$(tr '[:upper:]' '[:lower:]' <<<"$INPUT_VERBOSE") | ||
| case "$verbose_normalized" in | ||
| true|1|yes|on) | ||
| echo "ERROR: verbose output is disabled in this action to avoid logging matched secrets" >&2 | ||
| exit 1 | ||
| ;; | ||
| false|0|no|off|"") ;; | ||
| *) | ||
| echo "ERROR: invalid verbose value '$INPUT_VERBOSE' (expected: true or false)" >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
|
|
||
| paths=() | ||
| if [ -n "$INPUT_PATHS" ]; then | ||
| read -r -a paths <<<"$INPUT_PATHS" | ||
| fi | ||
|
|
||
| expanded_paths=() | ||
| if ((${#paths[@]})); then | ||
| for path_token in "${paths[@]}"; do | ||
| case "$path_token" in | ||
| *'*'*|*'?'*|*'['*) | ||
| matches=() | ||
| while IFS= read -r match; do | ||
| matches+=("$match") | ||
| done < <(compgen -G "$path_token" || true) | ||
|
|
||
| if ((${#matches[@]})); then | ||
| expanded_paths+=("${matches[@]}") | ||
| else | ||
| expanded_paths+=("$path_token") | ||
| fi | ||
| ;; | ||
| *) | ||
| expanded_paths+=("$path_token") | ||
| ;; | ||
| esac | ||
| done | ||
| fi | ||
|
|
||
| extra_args=() | ||
| if [ -n "$INPUT_ARGS" ]; then | ||
| read -r -a extra_args <<<"$INPUT_ARGS" | ||
| fi | ||
|
|
||
| if ((${#extra_args[@]})); then | ||
| for arg in "${extra_args[@]}"; do | ||
| case "$arg" in | ||
| --verbose|--verbose=*|-v*|--format|--format=*|-f|-f*|--output|--output=*|-o|-o*|--exit-mode|--exit-mode=*) | ||
| echo "ERROR: '$arg' is managed by action inputs and cannot be passed through args" >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| done | ||
| fi | ||
|
|
||
| if [ -n "$INPUT_OUTPUT" ]; then | ||
| report_path="$INPUT_OUTPUT" | ||
| report_dir=$(dirname -- "$report_path") | ||
| if [ "$report_dir" != "." ]; then | ||
| mkdir -p "$report_dir" | ||
| fi | ||
| else | ||
| report_path="$RUNNER_TEMP/keywatch-report.json" | ||
| mkdir -p "$(dirname -- "$report_path")" | ||
| fi | ||
|
|
||
| keywatch_args=(scan) | ||
| keywatch_args+=(--exit-mode "$INPUT_EXIT_MODE") | ||
| if ((${#extra_args[@]})); then | ||
| keywatch_args+=("${extra_args[@]}") | ||
| fi | ||
| keywatch_args+=(--output "$report_path") | ||
| if ((${#expanded_paths[@]})); then | ||
| keywatch_args+=(-- "${expanded_paths[@]}") | ||
| fi | ||
|
|
||
| rm -f -- "$report_path" | ||
|
|
||
| echo "Running KeyWatch scan..." | ||
| set +e | ||
| key-watch "${keywatch_args[@]}" | ||
| scan_status=$? | ||
| set -e | ||
|
|
||
| findings_count="unknown" | ||
| report_status="missing" | ||
| if [ -s "$report_path" ]; then | ||
| if jq -e '.findings | type == "array"' "$report_path" >/dev/null 2>&1; then | ||
| findings_count=$(jq -r '.findings | length' "$report_path") | ||
| report_status="ok" | ||
| else | ||
| report_status="malformed" | ||
| fi | ||
| fi | ||
|
|
||
| action_status=$scan_status | ||
| if [ "$report_status" != "ok" ] && [ "$scan_status" -eq 0 ]; then | ||
| echo "ERROR: KeyWatch exited successfully but the JSON report is $report_status" >&2 | ||
| action_status=2 | ||
| fi | ||
|
|
||
| { | ||
| echo "exit_code=$scan_status" | ||
| echo "findings_count=$findings_count" | ||
| } >> "$GITHUB_OUTPUT" | ||
|
|
||
| { | ||
| echo "## KeyWatch Scan Results" | ||
| echo "" | ||
| echo "| Metric | Value |" | ||
| echo "|--------|-------|" | ||
| echo "| Findings | $findings_count |" | ||
| echo "| Exit Code | $scan_status |" | ||
| echo "| Report | $report_status |" | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
|
|
||
| exit "$action_status" | ||
|
|
||
| branding: | ||
| icon: 'shield' | ||
| color: 'blue' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| name: Publish Docker Image | ||
|
|
||
| on: | ||
| push: | ||
| tags: ["v*"] | ||
| workflow_dispatch: | ||
| inputs: | ||
| tag: | ||
| description: 'Image tag' | ||
| required: true | ||
| default: 'latest' | ||
|
|
||
| permissions: | ||
| contents: read | ||
| packages: write | ||
|
|
||
| env: | ||
| REGISTRY: ghcr.io | ||
| IMAGE_NAME: ${{ github.repository }} | ||
|
|
||
| jobs: | ||
| build-and-push: | ||
| name: Build and push Docker image | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v7 | ||
|
|
||
| - name: Log in to GitHub Container Registry | ||
| uses: docker/login-action@v4.6.0 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Extract metadata (tags, labels) | ||
| id: meta | ||
| uses: docker/metadata-action@v6.2.0 | ||
| with: | ||
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | ||
| tags: | | ||
| type=semver,pattern={{version}} | ||
| type=semver,pattern={{major}}.{{minor}} | ||
| type=sha,format=short | ||
| type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }} | ||
|
|
||
| - name: Build and push Docker image | ||
| uses: docker/build-push-action@v7.3.0 | ||
| with: | ||
| context: . | ||
| push: true | ||
| tags: ${{ steps.meta.outputs.tags }} | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.