Thanks to visit codestin.com
Credit goes to github.com

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
44 changes: 44 additions & 0 deletions hookreceiver/github_signature_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/bash

# GitHub's example values from documentation
# https://docs.github.com/en/enterprise-cloud@latest/webhooks/using-webhooks/validating-webhook-deliveries
SECRET="It's a Secret to Everybody"
PLAIN_PAYLOAD="Hello, World!"
EXPECTED_SIGNATURE="757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17"

# Calculate our own signature to verify understanding
CALCULATED_SIGNATURE=$(echo -n "$PLAIN_PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')

# Display verification information
echo "=== GITHUB WEBHOOK SIGNATURE VALIDATION TEST ==="
echo "Secret: '$SECRET'"
echo "Payload: '$PLAIN_PAYLOAD'"
echo "Expected signature: $EXPECTED_SIGNATURE"
echo "Calculated signature: $CALCULATED_SIGNATURE"

if [ "$CALCULATED_SIGNATURE" = "$EXPECTED_SIGNATURE" ]; then
echo "✅ Our signature calculation matches GitHub's expected result"
else
echo "❌ Our signature calculation DOES NOT match GitHub's expected result"
echo " Please check the HMAC-SHA256 implementation"
exit 1
fi

# Save the payload to a file
echo -n "$PLAIN_PAYLOAD" > plain_payload.txt

echo -e "\n=== TESTING WEBHOOK VALIDATION ==="
echo "Sending exact 'Hello, World!' payload to the webhook receiver"
echo "Checking if your implementation can properly validate the signature"

# Send the webhook with exact plain payload, making sure Content-Type is treated correctly
curl -X POST http://localhost:8000/webhook \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: ping" \
-H "X-Hub-Signature-256: sha256=$EXPECTED_SIGNATURE" \
--data-binary "$PLAIN_PAYLOAD"

echo -e "\n\nDone! Check your webhook receiver for validation results."
echo "Clean up temporary files..."
rm plain_payload.txt
echo "Complete!"
23 changes: 17 additions & 6 deletions hookreceiver/hookreceiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def verify_signature(payload_body, secret_token, signature_header):
"""
if not signature_header:
abort(403, description="x-hub-signature-256 header is missing!")
app.logger.debug("x-hub-signature-256 is NOT present" )
hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256)
expected_signature = "sha256=" + hash_object.hexdigest()
if not hmac.compare_digest(expected_signature, signature_header):
Expand Down Expand Up @@ -119,19 +120,29 @@ def slurphook():
app.logger.debug("-" * 21)
app.logger.debug(f"Headers: {request.headers}")
app.logger.debug("-" * 21)
app.logger.debug(f"JSON payload:\n\n{json.dumps(request.json, indent=4)}")

# Get the raw request data for signature verification
payload_data = request.data

# Try to parse as JSON, but don't fail if it's not valid JSON
try:
payload_json = request.json
app.logger.debug(f"JSON payload:\n\n{json.dumps(payload_json, indent=4)}")
action_value = payload_json.get('action')
except:
# Handle non-JSON payloads (like plain text)
payload_json = {'raw_content': payload_data.decode('utf-8')}
app.logger.debug(f"Non-JSON payload:\n\n{payload_data.decode('utf-8')}")
action_value = None

if signature_header and config.hook_secret:
verify_signature(request.data, config.hook_secret, signature_header)
verify_signature(payload_data, config.hook_secret, signature_header)
else:
app.logger.debug("Skipping signature verification - no signature header or secret provided")

# Format headers for storage
headers_dict = dict(request.headers)
headers_formatted = json.dumps(headers_dict, indent=2)

# Extract action from the JSON, if available
action_value = request.json.get('action')

# Store webhook data in database
try:
Expand All @@ -142,7 +153,7 @@ def slurphook():
VALUES (?, ?, ?, ?, ?)
''', (
event_type,
json.dumps(request.json),
json.dumps(payload_json),
signature_header,
headers_formatted,
action_value
Expand Down
12 changes: 12 additions & 0 deletions hookreceiver/sendhook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Send a test webhook to the local server
# This script sends a test webhook to the local server using the curl command
# It sets the content type to application/json and the GitHub event type to push
# It also sets the X-Hub-Signature-256 header to a test value
# Finally, it sends the test webhook payload from the test.json file
# Set the url to your server https://your-server.com/webhook

curl -X POST http://localhost:8000/webhook \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: push" \
-H "X-Hub-Signature-256: sha256=test" \
-d @test.json
50 changes: 50 additions & 0 deletions hookreceiver/signature-validation-example.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/bin/bash

# https://docs.github.com/en/enterprise-cloud@latest/webhooks/using-webhooks/validating-webhook-deliveries

# Testing the webhook payload validation
# You can use the following secret and payload values to verify that your implementation is correct:
#
# secret: It's a Secret to Everybody
# payload: Hello, World!
# If your implementation is correct, the signatures that you generate should match the following signature values:
#
# signature: 757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17
# X-Hub-Signature-256: sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17
#
# # Test values for webhook validation
SECRET="It's a Secret to Everybody"
PLAIN_PAYLOAD="Hello, World!"
JSON_PAYLOAD="{\"message\": \"$PLAIN_PAYLOAD\"}"
EXPECTED_SIGNATURE="757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17"

# Display verification info
echo "Secret: $SECRET"
echo "Original plain text payload: $PLAIN_PAYLOAD"
echo "JSON payload (for webhook): $JSON_PAYLOAD"
echo "Expected signature (from plain text): $EXPECTED_SIGNATURE"

# Calculate signature for the JSON payload
JSON_SIGNATURE=$(echo -n "$JSON_PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
echo "JSON payload signature: $JSON_SIGNATURE"


echo -e "\n\n1. Testing JSON payload with original signature (will likely fail signature verification):"
echo "======================================================================================="
curl \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: ping" \
-H "X-Hub-Signature-256: sha256=$EXPECTED_SIGNATURE" \
-d "$JSON_PAYLOAD" \
"http://localhost:8000/webhook"

echo -e "\n\n2. Testing JSON payload with matching signature (should succeed):"
echo "=================================================================="
curl -X POST http://localhost:8000/webhook \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: ping" \
-H "X-Hub-Signature-256: sha256=$JSON_SIGNATURE" \
-d "$JSON_PAYLOAD" \
-v

echo -e "\n\nDone! Check your webhook receiver for the events."
38 changes: 36 additions & 2 deletions hookreceiver/templates/hookdb.html
Original file line number Diff line number Diff line change
Expand Up @@ -208,15 +208,15 @@ <h2>All Events</h2>
<div class="details">
<h3>Payload
<button class="copy-button"
onclick="copyToClipboard(this, '{{ formatted_payload|escape }}')">
onclick="copyToClipboard(this, document.querySelector('.details pre').textContent)">
Copy
</button>
</h3>
<pre>{{ formatted_payload }}</pre>

<h3>Headers
<button class="copy-button"
onclick="copyToClipboard(this, '{{ formatted_headers|escape }}')">
onclick="copyHeadersContent(this)">
Copy
</button>
</h3>
Expand Down Expand Up @@ -326,6 +326,40 @@ <h3>Headers
}, 2000);
});
}

function copyHeadersContent(button) {
// If we have a pre element for headers
const headersPre = document.querySelectorAll('.details pre')[1];
if (headersPre) {
navigator.clipboard.writeText(headersPre.textContent).then(() => {
button.textContent = 'Copied!';
setTimeout(() => {
button.textContent = 'Copy';
}, 2000);
});
return;
}

// If we have a table for headers
const headersTable = document.querySelector('.details table');
if (headersTable) {
let headerText = '';
const rows = headersTable.querySelectorAll('tr');
// Skip the first row (header row)
for (let i = 1; i < rows.length; i++) {
const cells = rows[i].querySelectorAll('td');
if (cells.length >= 2) {
headerText += `${cells[0].textContent}: ${cells[1].textContent}\n`;
}
}
navigator.clipboard.writeText(headerText).then(() => {
button.textContent = 'Copied!';
setTimeout(() => {
button.textContent = 'Copy';
}, 2000);
});
}
}
</script>
</body>
</html>
26 changes: 26 additions & 0 deletions hookreceiver/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"event_type": "push",
"repository": {
"name": "test-repo",
"owner": {
"login": "user123"
},
"url": "https://github.com/user123/test-repo"
},
"commits": [
{
"id": "abc123",
"message": "Update documentation",
"timestamp": "2023-09-10T14:32:15Z",
"author": {
"name": "Test User",
"email": "[email protected]"
}
}
],
"sender": {
"login": "user123",
"id": 12345
},
"timestamp": "2023-09-10T14:32:20Z"
}
41 changes: 41 additions & 0 deletions hookreceiver/test_webhook_validation.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

# Test values
SECRET="It's a Secret to Everybody"
PAYLOAD='{"message": "Hello, World!"}'
EXPECTED_SIGNATURE="757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17"

# Create a temporary JSON file with the test payload
echo "$PAYLOAD" > test_payload.json

# Generate the signature (for verification)
GENERATED_SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')

# Display verification info
echo "Secret: $SECRET"
echo "Payload: $PAYLOAD"
echo "Expected signature: $EXPECTED_SIGNATURE"
echo "Generated signature: $GENERATED_SIGNATURE"

if [ "$GENERATED_SIGNATURE" = "$EXPECTED_SIGNATURE" ]; then
echo "✅ Signatures match! Validation is working correctly."
else
echo "❌ Signatures don't match! Please check your implementation."
echo "Note: The expected signature was calculated with plain text 'Hello, World!'"
echo " but we're now using JSON format: $PAYLOAD"
fi

# Send the webhook
echo -e "\nSending webhook to http://localhost:8000/webhook...\n"

curl \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: ping" \
-H "X-Hub-Signature-256: sha256=$GENERATED_SIGNATURE" \
-d "$PAYLOAD" \
"http://localhost:8000/webhook" \

# Clean up
rm test_payload.json

echo -e "\nDone! Check your webhook receiver for the event."
52 changes: 52 additions & 0 deletions hookreceiver/verify_signature.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/bin/bash

# GitHub's example values from documentation
# https://docs.github.com/en/enterprise-cloud@latest/webhooks/using-webhooks/validating-webhook-deliveries
SECRET="It's a Secret to Everybody"
PLAIN_PAYLOAD="Hello, World!"
EXPECTED_SIGNATURE="757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17"

# Calculate our own signature to verify understanding
# The -n flag for echo is crucial - it prevents adding a newline character
CALCULATED_SIGNATURE=$(echo -n "$PLAIN_PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')

# Display verification information
echo "=== GITHUB WEBHOOK SIGNATURE VALIDATION TEST ==="
echo "Secret: '$SECRET'"
echo "Payload: '$PLAIN_PAYLOAD'"
echo "Expected signature: $EXPECTED_SIGNATURE"
echo "Calculated signature: $CALCULATED_SIGNATURE"

if [ "$CALCULATED_SIGNATURE" = "$EXPECTED_SIGNATURE" ]; then
echo "✅ SUCCESS! Our signature calculation matches GitHub's expected result"
echo "Your HMAC-SHA256 implementation is working correctly!"
else
echo "❌ FAILURE! Our signature calculation DOES NOT match GitHub's expected result"
echo "Please check your HMAC-SHA256 implementation"
echo
echo "Debug details:"
echo "- Check that the -n flag is used with echo to prevent trailing newline"
echo "- Make sure the exact string 'Hello, World!' is used (without quotes)"
echo "- Verify the secret is exactly: It's a Secret to Everybody"
echo "- Check that the HMAC is using SHA-256 algorithm"
fi

# Additional validation - show the exact bytes being hashed
echo
echo "=== HEX DEBUGGING ==="
echo "Payload as hex bytes:"
echo -n "$PLAIN_PAYLOAD" | xxd -p

# Show the openssl command with verbose output
echo
echo "=== OPENSSL VERBOSE OUTPUT ==="
echo -n "$PLAIN_PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -debug

echo
echo "=== Python equivalent command ==="
echo "import hmac, hashlib"
echo "secret = \"It's a Secret to Everybody\""
echo "payload = \"Hello, World!\""
echo "signature = hmac.new(secret.encode('utf-8'), payload.encode('utf-8'), hashlib.sha256).hexdigest()"
echo "print(signature)"
echo "print(signature == \"$EXPECTED_SIGNATURE\")"