diff --git a/projects/sqlalchemy-utils/Dockerfile b/projects/sqlalchemy-utils/Dockerfile new file mode 100644 index 000000000..caea3feec --- /dev/null +++ b/projects/sqlalchemy-utils/Dockerfile @@ -0,0 +1,22 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +FROM gcr.io/oss-fuzz-base/base-builder-python + +RUN git clone https://github.com/kvesteri/sqlalchemy-utils + +WORKDIR $SRC/sqlalchemy-utils +COPY build.sh fuzz_*.py $SRC/ diff --git a/projects/sqlalchemy-utils/build.sh b/projects/sqlalchemy-utils/build.sh new file mode 100644 index 000000000..adeff4123 --- /dev/null +++ b/projects/sqlalchemy-utils/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash -eu +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +# Build and install project (using current CFLAGS, CXXFLAGS). +pip3 install --upgrade pip +pip3 install sqlalchemy arrow colour babel +pip3 install -e . + +for fuzzer in $(find $SRC -name 'fuzz_*.py'); do + compile_python_fuzzer $fuzzer +done diff --git a/projects/sqlalchemy-utils/fuzz_db.py b/projects/sqlalchemy-utils/fuzz_db.py new file mode 100644 index 000000000..130230629 --- /dev/null +++ b/projects/sqlalchemy-utils/fuzz_db.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import atheris +import sys + +with atheris.instrument_imports(): + import sqlalchemy + from sqlalchemy import ( + create_engine, + insert, + Table, + Column, + Integer, + String, + MetaData + ) + import sqlalchemy_utils as utils + from sqlalchemy.sql import text + from sqlalchemy.exc import SQLAlchemyError + +@atheris.instrument_func +def TestInput(data): + if len(data) < 10: + pass + + fdp = atheris.FuzzedDataProvider(data) + + db_str = 'sqlite:///fuzz.db' + + metadata = MetaData() + fuzz_table = Table('fuzz_table', metadata, + Column('id', Integer, primary_key=True), + Column('Col1', String)) + + engine = create_engine(db_str) + metadata.create_all(engine) + + if not utils.database_exists(db_str): + utils.create_database(db_str) + assert utils.database_exists(db_str) + + try: + with engine.connect() as conn: + conn.execute(text(fdp.ConsumeString(100))) + except (SQLAlchemyError, UnicodeEncodeError) as e: + pass + except ValueError as e: + if "the query contains a null character" not in str(e): + raise e + + utils.drop_database(db_str) + assert not utils.database_exists(db_str) +def main(): + atheris.Setup(sys.argv, TestInput, enable_python_coverage=True) + atheris.Fuzz() + +if __name__ == "__main__": + main() diff --git a/projects/sqlalchemy-utils/fuzz_orm.py b/projects/sqlalchemy-utils/fuzz_orm.py new file mode 100644 index 000000000..6a3292d70 --- /dev/null +++ b/projects/sqlalchemy-utils/fuzz_orm.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import atheris +import sys + +with atheris.instrument_imports(): + import sqlalchemy + from sqlalchemy import Column, Integer, String, select, create_engine + from sqlalchemy.orm import declarative_base, Session + from sqlalchemy_utils import cast_if, escape_like + from sqlalchemy.exc import SQLAlchemyError + +Base = declarative_base() + +class FuzzTable(Base): + __tablename__ = "fuzz_table" + + id = Column(Integer, primary_key=True) + name = Column(String) + +@atheris.instrument_func +def TestInput(data): + if len(data) < 10: + pass + + fdp = atheris.FuzzedDataProvider(data) + + cast_if(FuzzTable.id, Integer) + cast_if(FuzzTable.name, Integer) + cast_if(FuzzTable.id, String) + cast_if(FuzzTable.name, String) + + cast_if(fdp.ConsumeInt(10), Integer) + cast_if(fdp.ConsumeString(10), Integer) + cast_if(fdp.ConsumeInt(10), String) + cast_if(fdp.ConsumeString(10), String) + + db_str = 'sqlite:///fuzz.db' + + engine = create_engine(db_str) + Base.metadata.create_all(engine) + + try: + with Session(engine) as session: + name_str = fdp.ConsumeString(20) + session.query(FuzzTable).filter(FuzzTable.name.ilike(escape_like(name_str))).all() + except SQLAlchemyError as e: + pass + +def main(): + atheris.Setup(sys.argv, TestInput, enable_python_coverage=True) + atheris.Fuzz() + +if __name__ == "__main__": + main() diff --git a/projects/sqlalchemy-utils/fuzz_type.py b/projects/sqlalchemy-utils/fuzz_type.py new file mode 100644 index 000000000..5d1b3b3a7 --- /dev/null +++ b/projects/sqlalchemy-utils/fuzz_type.py @@ -0,0 +1,116 @@ +#!/usr/bin/python3 +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import atheris +import sys + +with atheris.instrument_imports(): + from arrow import utcnow + from colour import Color + from uuid import uuid4 + + import sqlalchemy + from sqlalchemy.sql import text, select + from sqlalchemy.exc import SQLAlchemyError + from sqlalchemy import ( + create_engine, Integer, String, MetaData, + Table, Column, Sequence + ) + from sqlalchemy_utils import ( + ArrowType, ChoiceType, ColorType, CountryType, + Country, EmailType, JSONType, IPAddressType, + ScalarListType, URLType, UUIDType, WeekDays, + WeekDaysType + ) + + # The following imports are needed to make the pyinstaller + # executable work. + from babel import Locale + import babel.dates + import babel.numbers + + +@atheris.instrument_func +def TestInput(data): + if len(data) < 10: + pass + + fdp = atheris.FuzzedDataProvider(data) + + metadata = MetaData() + fuzz_table = Table('fuzz_table', metadata, + Column('id', Integer, Sequence('id_seq'), primary_key=True), + Column('Col1', String), + Column('Col2', ArrowType), + Column('Col3', ChoiceType( + [(u'c1', u'Choice 1'),(u'c2', u'Choice 2')] + )), + Column('Col4', ColorType), + Column('Col5', CountryType), + Column('Col6', EmailType), + Column('Col7', JSONType), + Column('Col8', IPAddressType), + Column('Col9', ScalarListType(int)), + Column('Col10', URLType), + Column('Col11', UUIDType(binary=False)), + Column('Col12', WeekDaysType) + ) + + engine = create_engine('sqlite:///fuzz.db') + metadata.create_all(engine) + try: + with engine.connect() as conn: + conn.execute(text(fdp.ConsumeString(100))) + ins = fuzz_table.insert().values( + Col1=fdp.ConsumeString(100), + Col2=utcnow(), + Col3=u'c1' if fdp.ConsumeBool() else u'c2', + Col4=Color("#{:02x}{:02x}{:02x}".format( + fdp.ConsumeIntInRange(0,255), + fdp.ConsumeIntInRange(0,255), + fdp.ConsumeIntInRange(0,255) + )), + Col5=Country('US'), + Col6=fdp.ConsumeString(20), + Col7={ + fdp.ConsumeString(2):fdp.ConsumeString(10), + fdp.ConsumeString(2):fdp.ConsumeString(10), + fdp.ConsumeString(2):fdp.ConsumeString(10) + }, + Col8="%d.%d.%d.%d"%( + fdp.ConsumeIntInRange(0,255), + fdp.ConsumeIntInRange(0,255), + fdp.ConsumeIntInRange(0,255), + fdp.ConsumeIntInRange(0,255) + ), + Col9=[fdp.ConsumeInt(8),fdp.ConsumeInt(8),fdp.ConsumeInt(8)], + Col10=fdp.ConsumeUnicode(20), + Col11=uuid4(), + Col12=WeekDays("{0:07b}".format(fdp.ConsumeIntInRange(0,31))) + ) + ins.compile() + conn.execute(ins) + except (SQLAlchemyError, UnicodeEncodeError) as e: + pass + except ValueError as e: + if "the query contains a null character" not in str(e): + raise e + +def main(): + atheris.Setup(sys.argv, TestInput, enable_python_coverage=True) + atheris.Fuzz() + +if __name__ == "__main__": + main() diff --git a/projects/sqlalchemy-utils/project.yaml b/projects/sqlalchemy-utils/project.yaml new file mode 100644 index 000000000..b9a0f4a29 --- /dev/null +++ b/projects/sqlalchemy-utils/project.yaml @@ -0,0 +1,13 @@ +fuzzing_engines: +- libfuzzer +homepage: https://github.com/kvesteri/sqlalchemy-utils +language: python +primary_contact: "contactme@kurtmckee.org" +main_repo: https://github.com/kvesteri/sqlalchemy-utils +sanitizers: +- address +- undefined +vendor_ccs: +- david@adalogics.com +- adam@adalogics.com +- arthur.chan@adalogics.com