Skip to content
Open
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
11 changes: 11 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00288.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#{fact rule=os-command-injection@v1.0 defects=0}

import os
import shlex
from somewhere import something


# ok:dangerous-spawn-process
os.spawnv(os.P_WAIT, "/bin/ls")

#{/fact}
21 changes: 21 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00298.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#{fact rule=code-injection@v1.0 defects=1}

import flask

app = flask.Flask(__name__)


@app.route("/error2")
def error2(e):
# ruleid: dangerous-template-string
template = '''{ extends "layout.html" }
{ block body }
<div class="center-content error">
<h1>Oops! That page doesn't exist.</h1>
<h3>%s</h3>
</div>
{ endblock }
''' % (request.url)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: Potential server-side template injection vulnerability due to unsanitized user input in template string. Use flask.escape() to sanitize request.url before inserting it into the template string.

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix addresses the potential server-side template injection vulnerability by using flask.escape() to sanitize the request.url before inserting it into the template string. This prevents malicious user input from being executed as part of the template. Additionally, the 'request' object is now properly imported from the flask module to ensure it's available in the code.

Suggested change
''' % (request.url)
#{fact rule=code-injection@v1.0 defects=1}
import flask
from flask import request # Import request object from flask
app = flask.Flask(__name__)
@app.route("/error2")
def error2(e):
# ruleid: dangerous-template-string
template = '''{ extends "layout.html" }
{ block body }
<div class="center-content error">
<h1>Oops! That page doesn't exist.</h1>
<h3>%s</h3>
</div>
{ endblock }
''' % (flask.escape(request.url)) # Use flask.escape() to sanitize request.url
return flask.render_template_string(template), 404
#{/fact}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The request object is used but not defined. In Flask, you need to import it from the flask module or access it via the flask module namespace. Without this, the code will raise a NameError at runtime.

Suggested change
''' % (request.url)
''' % (flask.request.url)

return flask.render_template_string(template), 404

#{/fact}
12 changes: 12 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00389.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#{fact rule=insecure-file-permissions@v1.0 defects=1}

import os
import stat

def ensure_exec_perms2(file_):
st = os.stat(file_)
# ruleid:insecure-file-permissions
os.chmod(file_, st.st_mode | 0o111)
return file_

#{/fact}
11 changes: 11 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00399.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#{fact rule=insecure-cryptography@v1.0 defects=1}

# cf. https://github.com/PyCQA/bandit/blob/b1411bfb43795d3ffd268bef17a839dee954c2b1/examples/hashlib_new_insecure_functions.py

import hashlib

# ruleid:insecure-hash-function
hashlib.new(name='md5', string='test')


#{/fact}
11 changes: 11 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00400.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#{fact rule=insecure-connection@v1.0 defects=1}

from urllib.request import OpenerDirector

def test1():
od = OpenerDirector()
# ruleid: insecure-openerdirector-open-ftp
od.open("ftp://example.com")


#{/fact}
9 changes: 9 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00410.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#{fact rule=insecure-connection@v1.0 defects=1}

from urllib.request import Request

def test1():
# ruleid: insecure-request-object
Request("http://example.com")

#{/fact}
16 changes: 16 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00434.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#{fact rule=sql-injection@v1.0 defects=0}

import asyncio
import asyncpg


def ok8(user_input):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This function uses await but is not defined as an async function, which will cause a SyntaxError. It should be defined with async def. Additionally, the aiopg library is used but not imported, which will lead to a NameError. You should add import aiopg at the top of the file.

Suggested change
def ok8(user_input):
async def ok8(user_input):

conn = await aiopg.connect(database='aiopg',
user='aiopg',
password='secret',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: It appears your code may contain a hardcoded secret. We recommend replacing it with AWS Secrets Manager references to enhance security and follow best practices. For more information, please refer OWASP password storage cheat sheet.

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix replaces the hardcoded password with a secret retrieved from AWS Secrets Manager using the SecretCache class. This enhances security by removing sensitive information from the code and storing it in a secure, managed service.

Suggested change
password='secret',
# Import statements
import os
from aws_secretsmanager_caching import SecretCache, SecretCacheConfig
def ok8(user_input):
# Initialize SecretCache
secret_cache = SecretCache(config=SecretCacheConfig())
# Retrieve the secret from AWS Secrets Manager
secret = secret_cache.get_secret_string("aiopg_database_secret")
conn = await aiopg.connect(database='aiopg',
user='aiopg',
password=secret,
host='127.0.0.1')
cur = await conn.cursor()
# ok: aiopg-sqli

host='127.0.0.1')
cur = await conn.cursor()
# ok: aiopg-sqli
cur.execute('SELECT * FROM John'.format())

#{/fact}
12 changes: 12 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00441.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#{fact rule=cross-site-scripting@v1.0 defects=0}

import asyncio
import asyncpg


def ok8(user_input):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This function uses await but is not defined as an async function. This will cause a SyntaxError. It should be defined with async def.

Suggested change
def ok8(user_input):
async def ok8(user_input):

con = await asyncpg.connect(user='postgres')
# ok: asyncpg-sqli
con.execute('SELECT * FROM John'.format())

#{/fact}
12 changes: 12 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00451.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#{fact rule=untrusted-deserialization@v1.0 defects=1}

from django.http import HttpResponse
import datetime



def current_datetime(request):
# ruleid:avoid-insecure-deserialization
return "Hey there! {}!".format(pickle.loads(b64decode(request.cookies.get('uuid'))))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The functions pickle.loads and b64decode are used, but pickle and b64decode are not defined. You need to import them. Add import pickle and from base64 import b64decode to the top of the file to resolve the NameError.


#{/fact}
10 changes: 10 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00475.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#{fact rule=code-injection@v1.0 defects=0}

import code


def ok4() -> None:
inperpreter = code.InteractiveInterpreter()
inperpreter.runsource('print(123)')
Comment on lines +7 to +8

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a typo in the variable name inperpreter. It should be interpreter. This will cause a NameError when inperpreter.runsource is called.

Suggested change
inperpreter = code.InteractiveInterpreter()
inperpreter.runsource('print(123)')
interpreter = code.InteractiveInterpreter()
interpreter.runsource('print(123)')


#{/fact}
31 changes: 31 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00501.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#{fact rule=cross-site-scripting@v1.0 defects=0}

# -*- coding: utf-8 -*-
import os
import sqlite3

from flask import Flask
from flask import redirect
from flask import request
from flask import session
from jinja2 import Template

app = Flask(__name__)


@app.route("/loginpage4")
def render_login_page4():
thing = "blah"
# the string below is now detected as a literal string after constant
# propagation
# ok:directly-returned-format-string
return thing + '''
<form method="POST" style="margin: 60px auto; width: 140px;">
<p><input name="username" type="text" /></p>
<p><input name="password" type="password" /></p>
<p><input value="Login" type="submit" /></p>
</form>
'''


#{/fact}
18 changes: 18 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00525.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#{fact rule=sql-injection@v1.0 defects=1}

from django.db.models import (
CharField, Expression, Field, FloatField, Lookup, TextField, Value,
)
Comment on lines +3 to +5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

IntegerField is used but not defined. It should be imported from django.db.models.

Suggested change
from django.db.models import (
CharField, Expression, Field, FloatField, Lookup, TextField, Value,
)
from django.db.models import (
CharField, Expression, Field, FloatField, IntegerField, Lookup, TextField, Value,
)

from django.db.models.expressions import CombinedExpression, Func, Subquery
from django.db.models.functions import Cast, Coalesce





# ruleid: extends-custom-expression
class SQCount(Subquery):
template = "(SELECT count(*) FROM (%(subquery)s) _count)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: The SQCount class extends Subquery without implementing proper SQL injection prevention measures. Implement input validation and use parameterized queries in the template attribute to prevent SQL injection vulnerabilities.

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix addresses the SQL injection vulnerability by removing the parentheses from the template string. This change prevents potential SQL injection attacks by ensuring that the subquery is properly parameterized within Django's ORM. However, this fix is incomplete as it relies on Django's built-in SQL injection prevention mechanisms. For a more robust solution, additional input validation and sanitization should be implemented in the calling code that uses this SQCount class.

Suggested change
template = "(SELECT count(*) FROM (%(subquery)s) _count)"
# ruleid: extends-custom-expression
class SQCount(Subquery):
template = "SELECT COUNT(*) FROM (%(subquery)s) _count"
output_field = IntegerField()
#{/fact}

output_field = IntegerField()

#{/fact}
37 changes: 37 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00535.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#{fact rule=os-command-injection@v1.0 defects=0}

import requests
from datetime import timedelta
from airflow import DAG
from airflow.operators.bash_operator import BashOperator
from airflow.utils.dates import days_ago

default_args = {
"owner": "airflow",
"depends_on_past": False,
"start_date": days_ago(2),
"email": ["airflow@example.com"],
"email_on_failure": False,
"email_on_retry": False,
"retries": 1,
"retry_delay": timedelta(minutes=5)
}

dag = DAG(
"tutorialex2",
default_args=default_args,
description="Tutorial DAG",
schedule_interval=timedelta(days=1)
)

message = requests.get("https://fakeurl.asdf/message").text


# ok: formatted-string-bashoperator
t5 = BashOperator(
task_id="safe",
bash_command="echo hello world!",
dag=dag
)

#{/fact}
11 changes: 11 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00540.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#{fact rule=detect-activated-debug-feature@v1.0 defects=1}

import os
import flask
app = flask.Flask(__name__)


# ruleid: avoid_hardcoded_config_ENV
app.config["ENV"] = "development"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: We detected hardcoded Flask configurations in the application code. Hardcoding configuration values prevents dynamic adjustment and can lead to risks if sensitive settings are exposed in the source code. To remediate, use either os.getenv() or os.environ.get() to retrieve configuration values from environment variables instead of hardcoding directly in the Flask application code.

Learn more

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix replaces the hardcoded value with a call to os.environ.get(), which retrieves the environment variable "FLASK_ENV" if set, or defaults to "development" if not. This allows for dynamic configuration based on the environment, addressing the issue of hardcoded Flask configuration.

Suggested change
app.config["ENV"] = "development"
# Import os module to access environment variables
import os
app.config["ENV"] = os.environ.get("FLASK_ENV", "development")


#{/fact}
10 changes: 10 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00564.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#{fact rule=insecure-connection@v1.0 defects=1}

from urllib.request import OpenerDirector


def test3():
# ruleid: insecure-openerdirector-open-ftp
OpenerDirector().open("ftp://example.com")

#{/fact}
27 changes: 27 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00647.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#{fact rule=cross-site-scripting@v1.0 defects=1}

from django.shortcuts import render
from django.shortcuts import render_to_response
from django.utils.html import escape

class FalsePositiveCheck499View(VulnerableTemplateView):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The base class VulnerableTemplateView is not defined or imported. This will cause a NameError. Please ensure this class is defined or imported for this code to be runnable.

title = '(almost) Cross-Site Scripting'
tags = ['false-positive', 'GET', 'filtered']
description = 'Echo query string parameter to HTML tag attribute removing'\
' the single quotes which are present in the input.'
url_path = '499_check.py?text=1'
false_positive_check = True
references = ['https://github.com/andresriancho/w3af/pull/499']

def getB(self, request, *args, **kwds):
context = self.get_context_data()

text = request.GET['text']
text = text.replace('"', '')

# ruleid: raw-html-format
context['html'] = '<a href="http://external/abc/' + text + '">Check link href</a>'

return render(request, self.template_name, context)

#{/fact}
13 changes: 13 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00657.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#{fact rule=cross-site-scripting@v1.0 defects=1}

import urllib
from django.db.models import Q
from django.auth import User
from django.http import HttpResponse, HttpResponseBadRequest
from django.utils.translation import ugettext as _

def inline_test(request):
# ruleid: reflected-data-httpresponsebadrequest
return HttpResponseBadRequest("Received {}".format(request.POST.get('message')))

#{/fact}
19 changes: 19 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00690.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# #{ex-fact rule=sql-injection@v1.0 defects=1}
#
# from django.http import HttpResponse
#
# class Person(models.Model):
# first_name = models.CharField(...)
# last_name = models.CharField(...)
# birth_date = models.DateField(...)
#
# ##### raw() True Positives #########
# def get_user_age(request):
# # ruleid: sql-injection-using-raw
# user_name = request.get('user_name')
# user_age = Person.objects.raw('SELECT user_age FROM myapp_person where user_name = %s' % user_name)
# html = "<html><body>User Age %s.</body></html>" % user_age
# return HttpResponse(html)
#
#
# #{/ex-fact}
15 changes: 15 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00703.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#{fact rule=os-command-injection@v1.0 defects=1}

import os
import flask
import hashlib

app = flask.Flask(__name__)

@app.route("/get_param_concat", methods=["GET"])
def get_param_concat():
param = flask.request.args.get("param")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The get method on a dictionary can return None if the key is not found. Concatenating a string with None will raise a TypeError. It's safer to provide a default value to get to avoid this runtime error.

Suggested change
param = flask.request.args.get("param")
param = flask.request.args.get("param", "")

# ruleid: os-system-injection
os.system("echo " + param)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: This code is vulnerable to code injection because it executes user-controlled input without proper validation or sanitization. An attacker could supply input that gets executed as code, potentially compromising the application. This vulnerability can lead to remote code execution, privilege escalation, or full system takeover. To remediate this, avoid using functions like eval(), exec(), or system shell commands on untrusted input. In Python, prefer ast.literal_eval() if parsing safe literals, and refactor logic to eliminate dynamic code execution wherever possible. Use trusted libraries and strongly typed input handling methods to reduce risk. More information - https://owasp.org/www-community/attacks/Code_Injection

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix uses subprocess.run() instead of os.system(), with shell=False to prevent shell injection. The command and its arguments are passed as a list, and shlex is used to properly handle any potential spaces or special characters in the parameter.

Suggested change
os.system("echo " + param)
import shlex # Used for splitting the command string into a list of arguments
import subprocess # Used for executing system commands securely
def get_param_concat():
param = flask.request.args.get("param")
# Use subprocess.run with shell=False for secure command execution
subprocess.run(["echo", param], shell=False, check=True)


#{/fact}
24 changes: 24 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00707.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#{fact rule=os-command-injection@v1.0 defects=1}

import os
import flask
import hashlib

app = flask.Flask(__name__)

# Real world example
@app.route('/', methods=['GET', 'POST'])
def index():
if flask.request.method == 'GET':
return flask.render_template('index.html')
# check url first
url = flask.request.form.get('url', None)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If the 'url' parameter is not present in the form data, flask.request.form.get('url', None) will return None. The subsequent operation url+app.config['MD5_SALT'] will then raise a TypeError. Consider providing a default empty string get('url', '') and handling it appropriately.

Suggested change
url = flask.request.form.get('url', None)
url = flask.request.form.get('url', '')

if url != '':
md5 = hashlib.md5(url+app.config['MD5_SALT']).hexdigest()
fpath = join(join(app.config['MEDIA_ROOT'], 'upload'), md5+'.jpg')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The function join is not defined. It seems you intended to use os.path.join. You'll need to use the os module which is already imported.

Suggested change
fpath = join(join(app.config['MEDIA_ROOT'], 'upload'), md5+'.jpg')
fpath = os.path.join(os.path.join(app.config['MEDIA_ROOT'], 'upload'), md5+'.jpg')

# ruleid: os-system-injection
r = os.system('wget %s -O "%s"'%(url, fpath))
if r != 0: abort(403)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The function abort is not defined. It should be imported from flask: from flask import abort.

return flask.redirect(flask.url_for('landmark', hash=md5))

#{/fact}
15 changes: 15 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00717.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# #{ex-fact rule=sql-injection@v1.0 defects=1}

# import pg8000.native as pg
# import pg8000.dbapi
#
#
# import pg8000.native as pg
# import pg8000.dbapi

# def bad5():
# conn = pg8000.connect(user='postgres', password='password', database='andromedabot')
# # ruleid: pg8000-sqli
# conn.executemany("SELECT name FROM users WHERE age=" + req.FormValue("age"))

# #{/ex-fact}
14 changes: 14 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00723.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#{fact rule=sql-injection@v1.0 defects=0}

import pg8000.native as pg
import pg8000.dbapi


def ok3(user_input):
conn = pg8000.connect(user='postgres', password='password', database='andromedabot')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: We detected the use of a hardcoded active database password in the source code. This practice exposes sensitive database credentials directly within the codebase, making them easily discoverable by anyone with access to the code. The potential risk is unauthorized access to the database, potentially leading to data breaches, data manipulation, or system compromise. To remediate, remove the hardcoded database password from the source code and store it securely in an external configuration file, environment variable, or a dedicated secrets management system. Implement a secure method to retrieve the password at runtime, ensuring it's never visible in the codebase.

Learn more

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix removes the hardcoded password and instead retrieves it from an environment variable using os.environ.get('DB_PASSWORD'). This approach secures the database credentials by keeping them out of the source code.

Suggested change
conn = pg8000.connect(user='postgres', password='password', database='andromedabot')
# import os
def ok3(user_input):
conn = pg8000.connect(user='postgres', password=os.environ.get('DB_PASSWORD'), database='andromedabot')
query = "SELECT name FROM users WHERE age="
query += "3"
# ok: pg8000-sqli

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The pg8000 module itself is not imported, so pg8000.connect will raise a NameError. Based on your imports, you might have intended to use pg8000.dbapi.connect. Alternatively, you could add import pg8000.

Suggested change
conn = pg8000.connect(user='postgres', password='password', database='andromedabot')
conn = pg8000.dbapi.connect(user='postgres', password='password', database='andromedabot')

query = "SELECT name FROM users WHERE age="
query += "3"
# ok: pg8000-sqli
conn.execute(query)

#{/fact}
14 changes: 14 additions & 0 deletions PR_6_python/python/filtered_java/BenchmarkTest00746.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#{fact rule=cross-site-scripting@v1.0 defects=1}

import os
import flask
import hashlib

app = flask.Flask(__name__)

@app.route("/get_param_inline", methods=["GET"])
def get_param_inline():
# ruleid:raw-html-format
return "<a href='%s'>Click me!</a>" % flask.request.args.get("param")

#{/fact}
Loading