Skip to content

Commit 9a232d2

Browse files
authored
Merge pull request #3 from DevSecOps-AppSec/hcl-sast
HCL SAST Poc
2 parents eb5a23a + c738091 commit 9a232d2

17 files changed

+376
-1
lines changed

.github/workflows/build.yml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Build
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
types: [opened, synchronize, reopened]
8+
jobs:
9+
sonarcloud:
10+
name: SonarCloud
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v3
14+
with:
15+
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
16+
- name: SonarCloud Scan
17+
uses: SonarSource/sonarcloud-github-action@master
18+
env:
19+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
20+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: 'Dependency Review'
2+
on:
3+
pull_request:
4+
types: [opened,synchronize]
5+
6+
permissions:
7+
contents: read
8+
9+
jobs:
10+
dependency-review:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: 'Checkout Repository'
14+
uses: actions/checkout@v4
15+
- name: 'Dependency Review'
16+
uses: actions/dependency-review-action@v4
17+

.github/workflows/hcl-codesweep.yaml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: "HCL AppScan CodeSweep"
2+
on:
3+
pull_request:
4+
types: [opened,synchronize]
5+
jobs:
6+
scan:
7+
runs-on: ubuntu-latest
8+
permissions:
9+
contents: read
10+
issues: write
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v2
14+
with:
15+
fetch-depth: 0
16+
- name: Run AppScan CodeSweep
17+
uses: HCL-TECH-SOFTWARE/appscan-codesweep-action@v2
18+
with:
19+
status: failure
20+
env:
21+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
22+

.github/workflows/snyk.yaml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Snyk Workflow for SAST & SCA
2+
on: push
3+
jobs:
4+
security:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- uses: actions/checkout@master
8+
- name: Run Snyk to check for vulnerabilities in dependencies
9+
uses: snyk/actions/python@master # See https://github.com/snyk/actions for other supported build tools/languages
10+
continue-on-error: true
11+
env:
12+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
13+
with:
14+
command: test --all-projects --json-file-output=snyk_dependencies.json
15+
- name: Run Snyk to check for vulnerabilities in code
16+
uses: snyk/actions/python@master # See https://github.com/snyk/actions for other supported build tools/languages
17+
continue-on-error: true
18+
env:
19+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
20+
with:
21+
command: code test --all-projects --json-file-output=snyk_code.json
22+
- name: Add Job Summary from Snyk reports
23+
uses: medly/[email protected]
24+
with:
25+
dependencies-report-path: snyk_dependencies.json # The file name of json file which is generated on snyk test
26+
code-report-path: snyk_code.json # The file name of json file which is generated on snyk code test

PAT

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[default]
2-
PAT = ***REMOVED***
2+
PAT = ***REMOVED***

XSS-test-1.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import http.server
2+
import urllib.parse
3+
4+
comments = []
5+
6+
class XSSHandler(http.server.BaseHTTPRequestHandler):
7+
def do_GET(self):
8+
if self.path == '/':
9+
self.send_response(200)
10+
self.send_header('Content-type', 'text/html')
11+
self.end_headers()
12+
self.wfile.write(b"""
13+
<html>
14+
<body>
15+
<h1>Comments</h1>
16+
<form action="/comment" method="post">
17+
<input type="text" name="comment">
18+
<input type="submit" value="Add Comment">
19+
</form>
20+
<h2>All Comments</h2>
21+
<ul>
22+
""")
23+
for comment in comments:
24+
self.wfile.write(f"<li>{comment}</li>".encode())
25+
self.wfile.write(b"""
26+
</ul>
27+
</body>
28+
</html>
29+
""")
30+
31+
def do_POST(self):
32+
if self.path == '/comment':
33+
content_length = int(self.headers['Content-Length'])
34+
post_data = self.rfile.read(content_length)
35+
comment = urllib.parse.parse_qs(post_data.decode())['comment'][0]
36+
comments.append(comment)
37+
self.send_response(303)
38+
self.send_header('Location', '/')
39+
self.end_headers()
40+
41+
if __name__ == '__main__':
42+
server = http.server.HTTPServer(('localhost', 8080), XSSHandler)
43+
print("Server running on http://localhost:8080")
44+
server.serve_forever()

XSS-test.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from flask import Flask, request
2+
3+
app = Flask(__name__)
4+
5+
@app.route('/')
6+
def home():
7+
return '''
8+
<html>
9+
<body>
10+
<h1>Search</h1>
11+
<form action="/search" method="get">
12+
<input type="text" name="query">
13+
<input type="submit" value="Search">
14+
</form>
15+
</body>
16+
</html>
17+
'''
18+
19+
@app.route('/search')
20+
def search():
21+
query = request.args.get('query')
22+
# XSS vulnerability: directly reflecting user input in the response
23+
return f'''
24+
<html>
25+
<body>
26+
<h1>Search Results for: {query}</h1>
27+
<p>No results found.</p>
28+
</body>
29+
</html>
30+
'''
31+
32+
if __name__ == '__main__':
33+
app.run(debug=True)

XXE.py

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import xml.etree.ElementTree as ET
2+
3+
def parse_xml(data):
4+
try:
5+
# XXE vulnerability
6+
tree = ET.ElementTree(ET.fromstring(data))
7+
root = tree.getroot()
8+
return [elem.tag for elem in root]
9+
except ET.ParseError as e:
10+
return f"An error occurred during XML parsing: {e}"
11+
12+
def main():
13+
print("Welcome to the simple XML parser.")
14+
user_input = input("Enter your XML data: ")
15+
16+
result = parse_xml(user_input)
17+
print(f"Parsed XML tags:\n{result}")
18+
19+
if __name__ == "__main__":
20+
main()

addWorkflow.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import sqlite3
2+
3+
def connect_to_db():
4+
# Hard-coded credentials (vulnerability)
5+
db_user = "admin"
6+
db_password = "***REMOVED***"
7+
database = "example.db"
8+
9+
conn = sqlite3.connect(database)
10+
return conn
11+
12+
def execute_query(conn, query):
13+
try:
14+
cursor = conn.cursor()
15+
cursor.execute(query)
16+
conn.commit()
17+
except Exception as e:
18+
# Improper error handling (vulnerability)
19+
print(f"An error occurred: {e}")
20+
21+
def main():
22+
conn = connect_to_db()
23+
24+
# SQL Injection vulnerability
25+
user_input = input("Enter your username: ")
26+
query = f"SELECT * FROM users WHERE username = '{user_input}'"
27+
28+
execute_query(conn, query)
29+
conn.close()
30+
31+
if __name__ == "__main__":
32+
main()

command-injection.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import subprocess
2+
import os
3+
4+
def execute_command(command):
5+
try:
6+
# Command Injection vulnerability
7+
output = subprocess.check_output(command, shell=True)
8+
return output.decode('utf-8')
9+
except subprocess.CalledProcessError as e:
10+
return f"An error occurred: {e}"
11+
12+
def main():
13+
print("Welcome to the simple command executor.")
14+
user_input = input("Enter the command you want to execute: ")
15+
16+
# Unsafe execution of user input
17+
result = execute_command(user_input)
18+
print(f"Command output:\n{result}")
19+
20+
if __name__ == "__main__":
21+
main()

deserialization.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import pickle
2+
import base64
3+
4+
def deserialize_data(serialized_data):
5+
try:
6+
# Insecure deserialization vulnerability
7+
data = pickle.loads(serialized_data)
8+
return data
9+
except Exception as e:
10+
return f"An error occurred during deserialization: {e}"
11+
12+
def main():
13+
print("Welcome to the simple deserialization service.")
14+
user_input = input("Enter base64-encoded serialized data: ")
15+
16+
try:
17+
serialized_data = base64.b64decode(user_input)
18+
result = deserialize_data(serialized_data)
19+
print(f"Deserialized data:\n{result}")
20+
except Exception as e:
21+
print(f"An error occurred: {e}")
22+
23+
if __name__ == "__main__":
24+
main()

directory-traversal.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import os
2+
3+
def read_file(file_path):
4+
try:
5+
with open(file_path, 'r') as file:
6+
return file.read()
7+
except Exception as e:
8+
return f"An error occurred while reading the file: {e}"
9+
10+
def main():
11+
print("Welcome to the simple file reader.")
12+
user_input = input("Enter the filename you want to read: ")
13+
14+
# Directory Traversal vulnerability
15+
file_content = read_file(user_input)
16+
print(f"File content:\n{file_content}")
17+
18+
if __name__ == "__main__":
19+
main()

requests-vuln.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Importing the requests library
2+
import requests
3+
4+
# URL to send a request to
5+
url = "http://example.com"
6+
7+
# Sending a GET request
8+
response = requests.get(url)
9+
10+
# Print the response text
11+
print(response.text)

requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
requests==2.19.1
2+

sonar-project.properties

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
sonar.projectKey=DevSecOps-AppSec_test_keys
2+
sonar.organization=devsecops-appsec
3+
4+
# This is the name and version displayed in the SonarCloud UI.
5+
#sonar.projectName=test_keys
6+
#sonar.projectVersion=1.0
7+
8+
9+
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
10+
#sonar.sources=.
11+
12+
# Encoding of the source code. Default is default system encoding
13+
#sonar.sourceEncoding=UTF-8

weak-encryption-algorithm-1.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from cryptography.fernet import Fernet
2+
3+
# Hardcoded key (vulnerability)
4+
key = b'12B7pZk9bVt7i9Th9F6j6vLf-8Yk8xZmHVOS3q7eYmI='
5+
cipher_suite = Fernet(key)
6+
7+
def encrypt_message(message):
8+
try:
9+
# Encrypting the message
10+
encrypted_text = cipher_suite.encrypt(message.encode())
11+
return encrypted_text
12+
except Exception as e:
13+
return f"An error occurred during encryption: {e}"
14+
15+
def decrypt_message(encrypted_message):
16+
try:
17+
# Decrypting the message
18+
decrypted_text = cipher_suite.decrypt(encrypted_message).decode()
19+
return decrypted_text
20+
except Exception as e:
21+
return f"An error occurred during decryption: {e}"
22+
23+
def main():
24+
print("Welcome to the insecure encryption demo.")
25+
choice = input("Do you want to (e)ncrypt or (d)ecrypt a message? ")
26+
27+
if choice.lower() == 'e':
28+
message = input("Enter the message to encrypt: ")
29+
encrypted_message = encrypt_message(message)
30+
print(f"Encrypted message: {encrypted_message}")
31+
elif choice.lower() == 'd':
32+
encrypted_message = input("Enter the encrypted message: ").encode()
33+
decrypted_message = decrypt_message(encrypted_message)
34+
print(f"Decrypted message: {decrypted_message}")
35+
else:
36+
print("Invalid choice. Please choose 'e' to encrypt or 'd' to decrypt.")
37+
38+
if __name__ == "__main__":
39+
main()

weak-encryption-algorithm.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
2+
from cryptography.hazmat.backends import default_backend
3+
import base64
4+
5+
# Hardcoded encryption key (vulnerability)
6+
key = b'0123456789abcdef' # 16-byte key for AES
7+
8+
def encrypt(plaintext):
9+
# Weak encryption using ECB mode (vulnerability)
10+
cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend())
11+
encryptor = cipher.encryptor()
12+
padded_plaintext = plaintext + (16 - len(plaintext) % 16) * ' '
13+
ciphertext = encryptor.update(padded_plaintext.encode()) + encryptor.finalize()
14+
return base64.b64encode(ciphertext).decode()
15+
16+
def decrypt(ciphertext):
17+
cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend())
18+
decryptor = cipher.decryptor()
19+
decrypted_data = decryptor.update(base64.b64decode(ciphertext)) + decryptor.finalize()
20+
return decrypted_data.decode().strip()
21+
22+
def main():
23+
print("Welcome to the insecure encryption service.")
24+
plaintext = input("Enter plaintext to encrypt: ")
25+
encrypted_text = encrypt(plaintext)
26+
print(f"Encrypted text: {encrypted_text}")
27+
28+
decrypted_text = decrypt(encrypted_text)
29+
print(f"Decrypted text: {decrypted_text}")
30+
31+
if __name__ == "__main__":
32+
main()

0 commit comments

Comments
 (0)