ソフトウェアのテスト中に、ユーザーがソフトウェアに対してこのような愚かなアクションを実行しないと想定できますか?


71

例:Webアプリケーションでフォームの機能テストを実行しているときに、異なる種類のランダムな入力値を入力してフィールドをテストします。

一般に、Webアプリケーションのユーザーとして、フィールドにランダムな値を実際に入力することはありません。

このような問題が本番環境で発生する可能性がはるかに低い場合、バグにつながる可能性のある/そうでない可能性のあるすべてのテストケースを組み込むことの使用は何ですか?

注:上記の例はサンプルケースにすぎません。このような問題は、あらゆる種類の機能/モジュールで発生する可能性があります。

この質問は、標準的な慣行が続くかどうか、または製品、ドメイン、その他すべての要因に完全に依存するかどうかを知るためだけに行っています。


4
おそらく関連性:サルのテスト、賛否両論あり
クリストフ

回答:


190

Webアプリケーションのフィールドにランダムな値を入力することはないかもしれませんが、まさにそれを行う人々は確かにそこにいます。

偶然にランダムに入力する人もいれば、意図的にアプリケーションを壊そうとする人もいます。どちらの場合でも、アプリケーションがクラッシュしたり、他の望ましくない動作をしたりすることは望ましくありません。
最初のタイプのユーザーについては、ユーザーに悪い体験を与え、ユーザーを追い払うかもしれないので、それは望ましくありません。
2番目のタイプのユーザーについては、彼らは通常、名誉ある意図を持っていないため、アクセスできないはずの情報へのアクセスを許可したり、本物のユーザーによるサービスへのアクセスを拒否したりしたくありません。

テストの標準的なプラクティスは、天気の良い場合が機能することを検証するだけでなく、潜在的な問題を見つけ、攻撃者がシステムに簡単にアクセスできないという自信を持たせるために、異常なエッジケースも調査することです。ランダム入力でアプリケーションが既にクラッシュしている場合、攻撃者が特別に細工された入力で何ができるかを知りたくありません。


16
そして、それをする人ではないものがあります。👽
小次郎

110
または、「O'Malley」、「姓名」、「Robert」など、実際の正式名称を入力しようとしている場合がありますドロップテーブルの学生;-」
l0b0

90
それとも本物の会社名、; DROP TABLE "COMPANIES";-LTD
ベン

25
最後の段落は、キーボードを横切る猫のランダムな入力でプログラムがクラッシュした場合、悪意のある入力でほぼ確実にクラッシュする(さらに悪い)ことを強調することで、さらに強化できると思います。
フィハグ

11
また、多くの人は、実際のデータ(名前、誕生日など)を提供したくないため、ランダムな入力を行います。また、コンピューターはアテンダントと同じくらいスマートで、「2016」ではなく「昨年」のように入力し、アプリケーションが人間のようにそれを処理することを期待する場合もあります。
ルアーン

102

何も想定しない

ユーザーがソフトウェアで偶然または故意に「愚かな」ことをしないと想定することはできません。ユーザーは誤って間違ったボタンを押したり、猫がキーボードの上を歩いたり、システムが誤動作したり、コンピューターが悪意のあるソフトウェアに乗っ取られたりする可能性があります。

さらに、ユーザー自身が悪意を持ち、意図的にあなたのソフトウェアを破壊する方法を探して、彼らが自分の利益のためにそれを悪用する方法を見つけることを望んでいます。悪用できないバグを見つけたとしても、見つけたものは何でも彼らを駆り立てて、QA手順が欠けていることを知って、攻撃できるものがないかシステムを調査します。

テストに関する限り、ランダムな入力を防ぐことは有用ですが、テスト入力を完全にランダムに選択する(つまり、ユースケースやエッジケースを特に考慮せずに)ことは無駄になります。テストの目的は、雇用主/クライアント/ユーザーの要件と期待に照らしてソリューションを検証することです。これは、すべてのエッジケースと境界条件、およびユーザーの予想されるワークフローに適合しない「縮退」ケースをターゲットにすることに集中する必要があることを意味します。

もちろん、後で修正する価値がないと判断したバグを明らかにするテストを実行することもできます。これはすべての種類の理由による可能性があります-バグはユーザーへの影響に比べて修正するには費用がかかりすぎるか、誰も使用していない機能のバグを発見する可能性がありますユーザーはそれを機能として扱っています。

または、バグを修正する時間を費やすことに商業的利益がない「専門家」ユーザーの限られた聴衆を持ついくつかのオーダーメイドのソフトウェアを書くかもしれません、それらのユーザーはバグのあるソフトウェア(例えば、診断ツール内部のITチームが使用しても収益が得られないため、たまにクラッシュした場合、それを修正するために必要な時間を支払う必要がなくなる可能性があります-ITチームに代わりにバグに対処するよう指示するだけです。

ただし、これらのバグについて知っている場合にのみ、これらの決定を行うことができます。たとえば、ユーザーがデータベース全体を消去する悪意のある入力を入力する可能性があります。このシナリオに対して明示的に保護およびテストしていない場合、これが発生するかどうかを確認する方法はありません。システムに未発見のバグを残すリスクは、それらのバグの1つが現実世界で明らかになり、ユーザーに大きな影響を与える場合、実際の問題に自分自身をさらす可能性があることを意味します。

そのため、バグを修正するかどうかの決定には、ソフトウェアの所有者(通常は給与を支払う人)からの入力が必要になる場合がありますが、バグをテストするかどうか、どのケースをテストするかについての決定は、エンジニアリング上の懸念事項です見積もりとプロジェクト計画に織り込まれ、目標は時間/お金/リソースの制約を考慮して、可能な限り完全に近い範囲のものにすべきです。


12
完全にランダムにテストすることは有用ではなく、考えられる限り多くのエッジケースを明示的にテストする必要がありますが、一定量のランダムファジングは、予測できない問題をチェックするのにも役立つ場合があります。
ショーンバートン

10
「バカはとても賢い人だから、バカに強いソフトウェアを書くのはとても難しい」という格言があります。だから、「ナンセンス」入力のテストをしてください!
ラルフクレーバーホフ

For example, a user may enter a malicious input which wipes the entire database - if you haven't explicitly protected against and tested for this scenario, then there's no way you can be sure whether or not this can happen.この XKCDコミックの小さなBobby Tablesが好きですか?;)
nick012000

12
「何も想定しない」これは良いアドバイスだと思います。
candied_orange

すべての「バグ」が「修正」ではないことを指摘していただきありがとうございます。エッジケースを認識することと、エッジケースの修正に時間とお金を費やすことには大きな違いがあります。Webフォームへの可能な入力を許可し、すべてのケースに対して一定の応答を設定することは素晴らしいことですが、それは特定のソフトウェアに関連しない可能性があります。入力でフロントエンドの数字のみが許可されているため、バックエンドで数字以外を受信することはできません。数字のみの形式で数字以外を使用するという潜在的なバグを「修正」することは、時間とお金の無駄です。
EvSunWoodard

60

考慮すべきいくつかの要因があります。これらのポイントを説明するために、ユーザーが特定のタスクに定義されたクォータのコンテキストで、タスクが使用できるディスク領域の量に関してパーセンテージを入力するフィールドの例を使用します。0%は、タスクがディスクに何も書き込めないことを意味します。100%は、タスクがすべてのディスク領域を占有できることを意味します。間の値は、その意味を意味します。

開発者として、許容値は[0、1、2、3、⋯99、100]であり、それ以外はすべてばかげていると考えているでしょう。ユーザーがこれらの「愚かな」値を入力し続けることができる理由を見てみましょう。

タイプミス

%^

ユーザーは値56 Shiftを入力していましたが、入力中に誤って押しました(たとえば、フランス語キーボードではShift数字を入力するために押す必要があり、ユーザーはフランス語キーボードとQWERTYを常に切り替えていたため)。

同様に、数値を取得することができます。数値は、その前後、またはその間にあります。

56q

ここで、ユーザーはおそらく数字を入力し、タブを押して次のフィールドに移動しました。を押す代わりに  ⇆  、ユーザーは隣接キーを押しました。

誤解と誤解

空の入力がおそらく最も一般的です。ユーザーは、フィールドがオプションであるか、このフィールドに何を入力するのかわからないことを想像しました。

56.5

ユーザーは、浮動小数点値は許容できると考えました。ユーザーが間違っていて、アプリケーションが整数値のみが受け入れられる理由を丁寧に説明するか、初期要件が間違っていたため、ユーザーに浮動小数点値を入力させるのが理にかなっています。

none

ユーザーは、タスクに必要なスペースを求められたときに、アプリが数字を期待していると誤解していました。これは、ユーザーインターフェイスの質が低いことを示している可能性があります。たとえば、ユーザーに「タスクに必要なディスク容量はどれくらいですか?」多くの意味。

150

この場合、ユーザーはパーセンテージの意味を誤解しました。たぶんユーザーは、タスクが現在使用されているスペースの150%を占有できることを伝えたいので、2 TBのディスクで100 GBが使用される場合、タスクは150 GBを使用できます。繰り返しますが、より優れたユーザーインターフェイスが役立ちます。たとえば、パーセント記号が付加された裸の入力フィールドを使用する代わりに、次のようにすることができます。

[____] % of disk space (2 TB)

ユーザーが入力を開始すると、その場でテキストが次のように変更されます。

[5___] % of disk space (102.4 GB of 2 TB)

表象

大きな数値または浮動小数点を含む数値は、異なる方法で表現できます。たとえば、番号1234.56は次のように記述できます1,234.56。文化に応じて、同じ数字のテキスト表現は異なります。フランス語では、同じ番号が次のように記述されます1 234,56。参照してください、あなたが1つを期待しないカンマ、およびスペース。

異なる国のユーザーは数字、日付、時刻などを書く習慣が異なるため、特定のロケールを使用する特定の形式を常に期待すると、遅かれ早かれ問題が発生します。

人間とコンピューター

Twenty-four

普通の人間はコンピューターと同じように考えていません。「24」実際の数字であり、PCから通知される内容とは無関係です。

(1)ほとんどのシステムはこのタイプの入力をまったく処理せず、(2)ほとんどすべてのユーザーは完全な文字で書かれた数字を入力することを想像しませんが、そのような入力が愚かであることを意味しません。ではフェイス3について、アラン・クーパーは、このような入力を処理していない点が、ヒトに適応するために、理想的には、インタフェースが正しくこれらの入力を処理することができるはずコンピュータのできないことを示すことができます。

アラン・クーパーの本に追加しなければならない唯一のことは、多くの場合、数字が誤って数字で書かれていることです。コンピューターがユーザーに間違いを犯すことを期待している(そして、正しく書くユーザーを容認しない)ことは迷惑です。

Unicode

5𝟨

Unicodeには驚きがあります。同じように見える文字は同じではありません。納得できない?"5𝟨" === "56"ブラウザの開発者ツールにコピーアンドペーストして、を押しEnterます。

これらの文字列が等しくない理由は、Unicode文字𝟨が文字と同じではないため6です。これにより、怒っている顧客が電話をかけて、アプリが機能していないことを伝え、合法的に見える入力のスクリーンショットを提供し、アプリが入力が無効であると主張する状況が生じます。

なぜだれかが数字のように見えるUnicode文字を入力するのでしょうか?ユーザーが意図せずに入力することは期待していませんが、別のソースからのコピーと貼り付けが原因である可能性があります。画面に表示されます。

結論

これらは、基本数値入力フィールドで得られるケースです。日付や住所など、より複雑なフォームを処理するために必要なものを想像させてください。

私の答えは、「愚かな」入力と呼ばれるものに焦点を当てています。テストは、ハッピーパスをチェックすることではありません。また、悪意のあるユーザーが意図的に奇妙なものを入力して破壊しようとしたときに、アプリが破壊されないことを確認することも重要です。これは、パーセンテージを求めている場合、ユーザーが1,000,000文字、負の数、またはbobby tableを含む文字列で応答しているときに何が起こるかをテストする必要があることを意味します


9
ああ、U + 1D7E8:数学的なSANS-SERIF DIGIT SIX。
アンドレアスレイブランド

23
他のユニコード文字について:日本語キーボードでは、通常の数字から全角数字に切り替えるのが非常に一般的であり、数字は漢字と同じ幅です。そのため、日本のユーザーは(英語ではなく)日本語入力があり、誤って全角数字を入力した可能性があります。
1

3
同じホモグリフの問題に関する5𝟨セクションを見る前に、実際に1 234,56文字列(U + 0020 SPACEの代わりにU + 00A0 NO-BREAK SPACEを使用)を期待していました。これはそれらの番号マーカーをコード化する適切な方法です(またはU + 202Fで)狭い休憩スペース、peroahps)。ユーザーに表示する前に、ロケールに従って数値をフォーマットするアプリケーションから値をコピーすると、非常に簡単に生成されます。
アンヘル

4
コピー&ペーストははるかに大きな問題です。共通するのは...ペーストスペース、改行、目に見えない文字をコピーすることです
Sulthan

7
@Arseni Mourzenkoあなたは幸運でなければなりません。例えばPDF&ペーストからのコピーはcircsによっては望ましくないかもしれない文字のすべての種類、例えば(FIなど)の合字、スマート引用符、途中またはASCIIのマイナスが望まれていた全角ダッシュなど貼りやすくなる
ロージーFを

12

ここにはなぜこれが重要であるかを説明する多くの良い答えがありますが、アプリケーションを賢明に保護する方法に関する多くのアドバイスはありません。「標準的なプラクティス」は、クライアントとサーバーの両方で堅牢な入力検証を使用することです。意味のない入力は簡単に防御できます。その特定のコンテキストで意味をなさないものを単に拒否します。たとえば、社会保障番号はダッシュと数字のみで構成されています。ユーザーが社会保障番号フィールドに入力する他のものはすべて安全に拒否できます。

作成するすべてのアプリケーションで2種類のテストを実行する必要があり、それぞれに異なる目的があります。独自のアプリケーションで行うテストポジティブテストです。その目的は、プログラムが機能すること証明することです。 テスターがアプリケーションでさらに行うテストは、ネガティブテストです。その目的は、プログラムが機能しないこと証明することです。 なぜこれが必要なのですか?あなたはあなた自身のソフトウェアをテストするのに最適な人ではないからです。結局のところ、あなたはそのことを書いたので、明らかにそれはすでに機能していますよね?

入力検証を記述するとき、検証が機能することを証明するために、ポジティブテストを使用します。テスターは、ランダムな入力を使用して、機能しないことを証明しようとします。ランダム入力の問題空間は本質的に無制限であることに注意してください。あなたの目標は、可能なすべての順列をテストすることではなく、無効な入力を拒否することで問題のスペースを制限することです。

また、プログラムへの入力を提供するのはエンドユーザーだけではないことに注意してください。作成するすべてのクラスには独自のAPIと有効な入力とみなされるものに対する独自の制約があるため、堅牢な検証(つまり「コードコントラクト」)もクラスにとって重要です。考えは、ソフトウェアを強化して、予期しない動作が可能な限りまれまたは存在しないようにすることです。

最後に、ワークフローが重要です。ユーザーが無意味なものを入力したためではなく、アプリケーション内で予期しない順序で処理を行ったため、アプリケーションが倒れるのを見てきました。アプリケーションはこの可能性を認識し、予期しないワークフローを適切に処理するか、ユーザーに指定された順序で操作を実行するように要求する必要があります。


特定の順序を期待するアプリケーションの一般的な例は、決して予約されなかったハンドルを解放する「ティアダウン」機能です。
wizzwizz4

2
残念ながら、標準的な方法では、意味をなさないものはすべて拒否し、ユーザーを混乱させ、イライラさせます。正しい方法は、入力が拒否された理由を正確に説明することです(たとえば、エラーメッセージ/フィードバックを使用して)。これにより、ユーザーは入力を修正して受け入れられるようになります。単純な「1〜100の整数を取得する」には、少なくとも4つの異なるエラーメッセージ(空の文字列、サポートされていない文字、大きすぎる、小さすぎる)が必要です。さらに、各ケースで適切なフィードバックが提供されることを確認するためのテスト。
ブレンダン

2
@Brendan:「1〜100の数字でなければなりません」というメッセージが1つだけ必要です ユーザーは、文字列が何であるか、または「サポートされていない文字」が何を意味するかを知らない(そして知る必要がない)。それらはプログラマの影響であり、ユーザーの助けではありません。
ロバートハーヴェイ

@RobertHarveyおそらく、「数字で構成された」という行に沿って、そのステートメントに何かを追加します。入力 "Seventy-Nine"は1〜100の数値ですが、ほとんどのプログラムで使用できる入力ではないためです。
Delioth

1
@Delioth:バカを直すことはできません。
ロバートハーベイ

11

通常、「ランダムな」値はランダムではありません。エッジケース、「不明な不明」をキャプチャしようとしています。

たとえば、#文字はアプリをクラッシュさせます。これを事前に知らないので、可能なすべての入力に対してテストケースを書くことは不可能です。しかし、テストを書いて、"¬!"£$%^&*()_+-=[]{};'#:@~,./<>?|\"それが壊れるかどうかを見ることができます


2
+1一見したところ、これらのランダムな文字がバグを見つける頻度は一見驚くほどです。ユーザー入力からのデータは、多くのコンポーネント/サービスを大量に通過できます。これは、チェーン内の一つの成分を取るだけではない右のバグを持っているシステムのためにそれを処理します。
Lan

4
特に 今モバイルキーボードすべてが顔文字があること
ユアン・

.Net開発者にとって、IntelliTestツール(以前はPexと呼ばれていました)は、コードパスを実行してエッジケースを見つけるのに非常に優れた方法です。これは、入力の検証および適切なコードカバレッジの取得に特に役立ちます。
ジェームズスネル

7

私はかつてプログラムを書いたことがあり、それを60人の学生がいるラボでライブテストしました。私は60台のコンピューター画面の後ろに立っていて、それらがそれを使用しているのを見ました。彼らがしたとんでもないことの量は、髪を上げることでした。私は彼らの「創造性」を見て汗だくになりました。彼らは、一人一人が一生のうちに空想することができる以上のことをしました。もちろん、そのうちの1人がそれを破りました。

その後、アプローチに従います。 if "a very specific use case" do, else show error

複数のユースケースがある場合、それらを厳密に定義し、上記のチェーンを作成します。


1
ただし、これらの特定のユースケースは非常に具体的すぎる可能性があります。有効な入力のスペースを常に過小評価しています。(オハラ、ローカルにフォーマットされた小数など)。マイナス金利を処理するために準備された財務ルーチンはいくつありますか?
グラン

6

説明しているのは、ファジングまたはファズテストです。システムにランダムで無効な入力を投げて、何が起こるかを確認します。ユーザーが実行することを期待しているため、これを実行しません。独自の仮定とバイアスを公開して、システムのエッジにストレスを与えて何が起こるかを確認します。

人間が書いた通常のテスト入力には、仮定とバイアスが伴います。これらのバイアスは、特定のクラスのバグである可能性がありますが、テストでは発見されません。

たとえば、入力の大部分がASCIIセーフなUnicodeの範囲内にある場合、コード内の文字エンコードに関する仮定は実行されません。または、常に特定のサイズよりも小さいため、固定サイズのフィールドまたはバッファがヒットしません。または、ユーザー入力がシェルに送られたり、安全でない方法でクエリを作成するために使用されたりすることを公開する驚くべき方法で解釈される特殊文字があるかもしれません。または、「ハッピーパス」テストが多すぎて、エラー処理を実行しようとする試みが不十分な場合があります。

ファジングには、入力に関する先入観がありません。「有効な」入力を可能な限り組み合わせて、システムを酷使します。Unicode、ASCII、大、小、および多くのエラー。システムはそれらすべてに適切に応答する必要があります。クラッシュすることはありません。ユーザーは、何が問題で、どのように修正するかについて、常に何らかの理にかなったメッセージを受け取る必要があります。ガベージイン/ガベージアウトではなく、ガベージイン/エラーアウトです。

「実際のユーザーはそれを行わない」ため、結果として生じる爆発を却下するかもしれませんが、それは演習のポイントを逃します。ファジングは、可能な入力に関するバイアスを排除する安価な方法です。これは、ユーザーがシステムで実行しようとする奇妙なことをすべて投げる安価な方法です。エンジニアとしての仕事は、システムが正常に機能することを確認することです。


さらに、ファジングの「入力」はユーザーだけのものではありません。サードパーティサービスへのAPIクエリの結果である可能性がありますが、それが混乱した結果の送信を開始したらどうなるでしょうか システムはそれをどのように処理しますか?適切なシステムは、コンポーネントが不良になったことを管理者に警告する必要があります。不適切なシステムは、悪いクエリを静かに拒否するか、さらに悪いことに、それを良いデータとして受け入れます。

最後に、一部のユーザーは悪意のあるユーザーです。システムのファジーテストを行わない場合、他の誰かが行います。彼らは、システムの端にある一般的な間違いを探り、それらをセキュリティホールとして使用しようとします。ファズテストはこれをある程度シミュレートでき、問題になる前に発見されたセキュリティホールに対処できます。


そして、ありますクイックチェックと同様のことを行うテストツール
icc97

4

高品質の製品を作成することが目的の場合、ユーザーが物理的に送信できるすべての可能な入力タイプをテストします。それ以外の場合は、誰かがテストの必要がないと感じた入力の1つのタイプを送信する日を待っています。

私が働いていた地方自治体での新しい電子オークションソフトウェアの大規模なデモンストレーションで、私のマネージャーは、負の値でオークション入札を行った場合に何が起こったのかを見る必要性を感じたと(確かにいたずらで)決めました。驚いたことに、オークションソフトウェアはこの無意味な入札を許可し、オークションプロセス全体が停止しました。示されているオークションのタイプでは、負の金額を送信することは許可されていません。

集められた調達および財務担当者の大規模なグループの一部は、無意味な価値を提出するために私のマネージャーに悩まされていました。しかし、私を含む他の人たちは、このような明らかなタイプの無効な入力をテストして拒否することに失敗したため、ソフトウェア開発者に悩まされていました。ソフトウェアが他のタイプの無効な入力(コードインジェクションの試み、データベーステーブルで表現できないエキゾチックな文字など)をそらすことにどれだけ弱かったか想像できます。

それは私次第でしたが、私はソフトウェアを返品し、目的に合わないと判断していました。弱いソフトウェア製品と強いソフトウェア製品の違いは、対象となるテストのレベルです。


2
test every possible type of input that a user will be physically able to submit.-その問題空間は本質的に無限であり、すべてをテストしようとして時間を無駄にしている。負の入力の確認は、単一の分岐です。それは賢明なだけでなく、有能な開発者にも期待されています。このような検証が機能することを証明するために、すべての負の数をチェックする必要はありません。
ロバートハーベイ

13
それが、すべての可能な入力ではなく、あらゆるタイプの入力を言った理由です。そして、繰り返しますが、すべてのタイプの入力をテストしないと、ユーザーは最終的にテストを行います。
アルカノン

1

例:Webアプリケーションでフォームの機能テストを実行しているときに、異なる種類のランダムな入力値を入力してフィールドをテストします。

はい。これは一種のテストですが、機能テストではありません。これは、ストレステストと呼ばれるものです。システムに圧力をかけて、システムが処理できるかどうかを確認する行為です。

このような問題が本番環境で発生する可能性がはるかに低い場合、バグにつながる可能性のある/そうでない可能性のあるすべてのテストケースを組み込むことの使用は何ですか?

ソフトウェアのストレステストを行うとき、ソフトウェアの限界とは何かの境界を見つけようとしています。

テストは本質的に徹底的なものです。使用制限、ブレークポイント、すべての論理ブランチを確認するか、部分的な障害がシステム全体に与える影響を確認する必要がある場合。

すべての機能テストに合格させることができますが、それでもストレステストに失敗します。

この質問は、標準的な慣行が続くかどうか、または製品、ドメイン、その他すべての要因に完全に依存するかどうかを知るためだけに行っています。

はい、これは標準的な方法です。

ソフトウェアのテストとは、予想される動作について質問することであり、すべてのテストに合格すると、ソフトウェアが意図したとおりに動作することを伝えます。これが、テストが更新プログラムの展開に関する適切な前提条件となる理由です。

ストレステストでは、明確な特定の合格または不合格のインジケータは提供されません。結果はより有益です。システムが何を処理できるかを示し、その情報から決定を下します。

開発の次の段階に進むために合格する必要があるストレステストの特定の目標を定義できます。これらは品質保証プロセスの一部として含めることができますが、環境の変化はストレステストの結果を変える可能性があります。したがって、人々はさまざまな時間ストレステストを実行して、システムが変化する条件をどのように処理するかを確認します。

つまり、ソフトウェアの新しいバージョンを展開するたびにストレステストを実行することはできません。これは、すべてが後でストレステストに合格することを意味します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.