Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 5 additions & 63 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -1,69 +1,11 @@
#!/bin/sh
# Blocks commits that carry AWS credentials.
#
# examples/network/providerconfig.yaml holds a ProviderConfig and its secret
# together so automated testing can apply both at once. That means the file is
# tracked with placeholders and is meant to be edited locally — one `git add .`
# away from publishing real credentials.
# Refuses to commit AWS credentials.
#
# Enable with:
#
# git config core.hooksPath .githooks
#
# Checks staged content, not the working tree, since that is what a commit
# records. `git commit --no-verify` bypasses it.
#
# Deliberately no `set -e`: this script manages its own exit status, and an
# AND-OR list returning non-zero must not abort the scan early.
set -u

fail=0
note() { printf '%s\n' "$*" >&2; }

staged=$(git diff --cached --name-only --diff-filter=ACM)
[ -n "$staged" ] || exit 0

# 1. The credentials file must still hold its placeholders when committed.
guarded='examples/network/providerconfig.yaml'
if printf '%s\n' "$staged" | grep -qx "$guarded"; then
if ! git show ":$guarded" 2>/dev/null | grep -q 'REPLACE_ME'; then
note "BLOCKED: $guarded is staged with its REPLACE_ME placeholders removed."
note ""
note " Real credentials must never be committed. To keep your local edit"
note " out of git for good:"
note ""
note " git restore --staged $guarded"
note " git update-index --skip-worktree $guarded"
note ""
fail=1
fi
fi

# 2. Belt and braces: an AWS key pasted into any staged file, not just that one.
# grep -a so a binary blob is scanned rather than skipped. This file is
# exempt: it necessarily contains the patterns it searches for.
offenders=$(
printf '%s\n' "$staged" | while IFS= read -r f; do
[ -n "$f" ] || continue
if [ "$f" = '.githooks/pre-commit' ]; then continue; fi
blob=$(git show ":$f" 2>/dev/null) || continue
if printf '%s' "$blob" | grep -aEq '(AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}'; then
printf '%s\taccess key ID\n' "$f"
fi
if printf '%s' "$blob" | grep -aEq 'aws_secret_access_key[[:space:]]*=[[:space:]]*[A-Za-z0-9/+=]{40}'; then
printf '%s\tsecret access key\n' "$f"
fi
done
)

if [ -n "$offenders" ]; then
printf '%s\n' "$offenders" | while IFS="$(printf '\t')" read -r f kind; do
note "BLOCKED: $f looks like it contains an AWS $kind."
done
fail=1
fi

if [ "$fail" -ne 0 ]; then
note "Commit aborted. Use --no-verify only if you are certain."
fi
exit "$fail"
# The check itself lives in hack/check-credentials.sh, which CI runs over the
# whole tree, so the hook and CI cannot drift apart. `git commit --no-verify`
# bypasses this.
exec "$(git rev-parse --show-toplevel)/hack/check-credentials.sh" --staged
18 changes: 17 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ jobs:
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "Computed version: ${VERSION}"

# examples/network/providerconfig.yaml is tracked with placeholders and meant
# to be edited locally, so real credentials are one `git add .` away from
# being published. The pre-commit hook in .githooks/ catches that, but hooks
# are opt-in per clone — this is the check that cannot be skipped.
credentials:
runs-on: *default-runner
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Check for AWS credentials
run: hack/check-credentials.sh

# TEMPORARY: build the crossplane CLI from source, because TypeScript
# embedded functions are not in a released CLI yet. Building it once and
# sharing the binary keeps the jobs below from each pulling the CLI's whole
Expand All @@ -60,7 +73,10 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: stevendborrelli/cli
ref: project-typescript-support
# Pinned to a commit rather than the branch: the branch is force-pushed
# as the PR is revised, which would silently change what a release
# builds. Bump this when picking up new CLI changes.
ref: 0d19745dc682ca02085f59bc8d9dcf5c37538421

# Take the Go version from the CLI's own go.mod so this keeps working as
# the CLI moves forward.
Expand Down
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ kind: Configuration
metadata:
name: configuration-aws-network
spec:
package: xpkg.upbound.io/upbound/configuration-aws-network-ts:v0.2.0
package: xpkg.upbound.io/upbound/configuration-aws-network-ts:v0.3.0
```

Verify the package is healthy. If not, run `kubectl describe configuration.pkg configuration-aws-network`.

```sh
$ kubectl get configuration.pkg configuration-aws-network
NAME INSTALLED HEALTHY PACKAGE AGE
configuration-aws-network True True xpkg.upbound.io/upbound/configuration-aws-network-ts:v0.2.0 18m
configuration-aws-network True True xpkg.upbound.io/upbound/configuration-aws-network-ts:v0.3.0 18m
```

### Configuring AWS Authentication
Expand Down Expand Up @@ -326,16 +326,19 @@ so that automated testing can bring both up with a single `--extra-resources` ar
> file up again later — say to change the placeholders themselves — reverse it with
> `--no-skip-worktree`.

There is also a pre-commit hook in [.githooks/](.githooks/) that refuses any commit staging
this file without its placeholders, or staging an AWS access key or secret key in any other
file. It is worth enabling once per clone:
[hack/check-credentials.sh](hack/check-credentials.sh) refuses this file without its
placeholders, or an AWS access key or secret key in any other file. It runs in two places:

```bash
git config core.hooksPath .githooks
```
- **CI**, over every tracked file, in the `credentials` job. This one cannot be skipped, and is
what actually keeps credentials off `main`.
- **A pre-commit hook**, over staged content, so you find out before the push rather than after.
Hooks are not installed by cloning, so enable it once per clone:

```bash
git config core.hooksPath .githooks
```

Hooks are not installed by cloning, so this is opt-in — it protects you, not the repository.
`git commit --no-verify` bypasses it.
`git commit --no-verify` bypasses the hook — but not CI.

Once it is up, apply the example as normal:

Expand Down Expand Up @@ -372,7 +375,7 @@ crossplane project build
it references:

```bash
crossplane project push --tag v0.2.0
crossplane project push --tag v0.3.0
```

Both run in [CI](.github/workflows/ci.yaml) on every push.
Expand Down
4 changes: 2 additions & 2 deletions functions/network/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion functions/network/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "configuration-aws-network-ts-function",
"version": "0.2.0",
"version": "0.3.0",
"description": "Composes an AWS network (VPC, subnets, route tables, gateway, security groups).",
"license": "Apache-2.0",
"type": "module",
Expand Down
84 changes: 84 additions & 0 deletions hack/check-credentials.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/bin/sh
# Refuses AWS credentials in this repository.
#
# examples/network/providerconfig.yaml holds a ProviderConfig and its secret
# together so automated testing can apply both at once. That means it is
# tracked with placeholders and meant to be edited locally, which puts real
# credentials one `git add .` away from being published.
#
# Two modes, so the pre-commit hook and CI share one definition of what a
# credential looks like:
#
# check-credentials.sh scan tracked files on disk (CI)
# check-credentials.sh --staged scan staged blobs (pre-commit)
#
# Deliberately no `set -e`: this script manages its own exit status, and an
# AND-OR list returning non-zero must not abort the scan early.
set -u

mode=worktree
[ "${1:-}" = '--staged' ] && mode=staged

guarded='examples/network/providerconfig.yaml'
# This file necessarily contains the patterns it searches for.
self='hack/check-credentials.sh'

fail=0
note() { printf '%s\n' "$*" >&2; }

if [ "$mode" = staged ]; then
files=$(git diff --cached --name-only --diff-filter=ACM)
read_file() { git show ":$1" 2>/dev/null; }
else
files=$(git ls-files)
read_file() { cat "$1" 2>/dev/null; }
fi

[ -n "$files" ] || exit 0

# 1. The credentials file must still hold its placeholders.
if printf '%s\n' "$files" | grep -qx "$guarded"; then
if ! read_file "$guarded" | grep -q 'REPLACE_ME'; then
note "BLOCKED: $guarded no longer has its REPLACE_ME placeholders."
note ""
note " Real credentials must never be committed. To keep a local edit"
note " out of git for good:"
note ""
note " git restore --staged $guarded"
note " git update-index --skip-worktree $guarded"
note ""
fail=1
fi
fi

# 2. An AWS key in any file, not just that one. grep -a so a binary blob is
# scanned rather than skipped.
offenders=$(
printf '%s\n' "$files" | while IFS= read -r f; do
[ -n "$f" ] || continue
if [ "$f" = "$self" ]; then continue; fi
blob=$(read_file "$f") || continue
if printf '%s' "$blob" | grep -aEq '(AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}'; then
printf '%s\taccess key ID\n' "$f"
fi
if printf '%s' "$blob" | grep -aEq 'aws_secret_access_key[[:space:]]*=[[:space:]]*[A-Za-z0-9/+=]{40}'; then
printf '%s\tsecret access key\n' "$f"
fi
done
)

if [ -n "$offenders" ]; then
printf '%s\n' "$offenders" | while IFS="$(printf '\t')" read -r f kind; do
note "BLOCKED: $f looks like it contains an AWS $kind."
done
fail=1
fi

if [ "$fail" -ne 0 ]; then
if [ "$mode" = staged ]; then
note "Commit aborted. Use --no-verify only if you are certain."
else
note "Credential check failed."
fi
fi
exit "$fail"